Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse files- .build_counter +1 -1
- 2.deploy.sh +77 -0
- public/scripts/LiveSync.js +46 -6
.build_counter
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
|
|
|
|
| 1 |
+
22
|
2.deploy.sh
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env bash
|
| 2 |
+
|
| 3 |
+
# --- CONFIGURATION ---
|
| 4 |
+
WORKFLOW_ID="221629785" # Build Android (Bundled HuggingFace)
|
| 5 |
+
ARTIFACT_NAME="tldraw-bundled-hf-debug.apk"
|
| 6 |
+
TARGET_DIR="/storage/emulated/0/Download/MY-T-APP"
|
| 7 |
+
COUNTER_FILE=".build_counter"
|
| 8 |
+
|
| 9 |
+
# 1. RUN HUGGING FACE UPLOAD
|
| 10 |
+
echo "🚀 Starting Hugging Face Upload..."
|
| 11 |
+
~/.hf-cli/venv/bin/hf upload Jaimodiji/my-multiplayer-app . . \
|
| 12 |
+
--repo-type space \
|
| 13 |
+
--exclude "node_modules/*" \
|
| 14 |
+
--exclude "dist/*" \
|
| 15 |
+
--exclude ".wrangler/*" \
|
| 16 |
+
--exclude ".git/*" \
|
| 17 |
+
--exclude ".env" \
|
| 18 |
+
--exclude ".dev.vars" \
|
| 19 |
+
--exclude "temp/*" \
|
| 20 |
+
--exclude "restored_files/*" \
|
| 21 |
+
--exclude "*.sqlite" \
|
| 22 |
+
--exclude "*.log"
|
| 23 |
+
|
| 24 |
+
# --- OPTION: EXIT HERE IF HF ONLY ---
|
| 25 |
+
if [[ "$1" == "hf" ]] || [[ "$1" == "--hf-only" ]]; then
|
| 26 |
+
echo "✅ Hugging Face upload complete. Exiting (hf-only mode)."
|
| 27 |
+
exit 0
|
| 28 |
+
fi
|
| 29 |
+
|
| 30 |
+
# 2. GIT INCREMENTAL COMMIT
|
| 31 |
+
if [ ! -f "$COUNTER_FILE" ]; then echo 0 > "$COUNTER_FILE"; fi
|
| 32 |
+
CURRENT_COUNT=$(cat "$COUNTER_FILE")
|
| 33 |
+
NEXT_COUNT=$((CURRENT_COUNT + 1))
|
| 34 |
+
echo "$NEXT_COUNT" > "$COUNTER_FILE"
|
| 35 |
+
|
| 36 |
+
COMMIT_MSG="test-dev-$NEXT_COUNT"
|
| 37 |
+
echo "📦 Committing to Git with message: '$COMMIT_MSG'..."
|
| 38 |
+
|
| 39 |
+
git add .
|
| 40 |
+
git commit -m "$COMMIT_MSG"
|
| 41 |
+
git push
|
| 42 |
+
|
| 43 |
+
# 3. TRIGGER GITHUB ACTION
|
| 44 |
+
echo "🎬 Triggering GitHub Action..."
|
| 45 |
+
gh workflow run "$WORKFLOW_ID"
|
| 46 |
+
echo "⏳ Waiting for run to start..."
|
| 47 |
+
sleep 5
|
| 48 |
+
RUN_ID=$(gh run list --workflow "$WORKFLOW_ID" --limit 1 --json databaseId -q '.[0].databaseId')
|
| 49 |
+
echo "👀 Watching Run ID: $RUN_ID"
|
| 50 |
+
|
| 51 |
+
gh run watch "$RUN_ID" --exit-status
|
| 52 |
+
if [ $? -ne 0 ]; then
|
| 53 |
+
echo "❌ Build Failed on GitHub! Check logs."
|
| 54 |
+
exit 1
|
| 55 |
+
fi
|
| 56 |
+
|
| 57 |
+
# 4. DOWNLOAD AND MOVE ARTIFACT
|
| 58 |
+
echo "📥 Downloading artifact..."
|
| 59 |
+
mkdir -p "$TARGET_DIR"
|
| 60 |
+
mkdir -p temp_artifact
|
| 61 |
+
|
| 62 |
+
# gh run download AUTOMATICALLY unzips the file into the directory
|
| 63 |
+
gh run download "$RUN_ID" -n "$ARTIFACT_NAME" --dir temp_artifact
|
| 64 |
+
|
| 65 |
+
echo "📂 Moving APK to Android storage..."
|
| 66 |
+
# Find the apk (wherever it is inside the artifact) and move it
|
| 67 |
+
FOUND_COUNT=$(find temp_artifact -name "*.apk" -exec mv {} "$TARGET_DIR/" \; -print | wc -l)
|
| 68 |
+
|
| 69 |
+
# Cleanup
|
| 70 |
+
rm -rf temp_artifact
|
| 71 |
+
|
| 72 |
+
if [ "$FOUND_COUNT" -eq "0" ]; then
|
| 73 |
+
echo "❌ ERROR: No APK file was found in the downloaded artifact."
|
| 74 |
+
exit 1
|
| 75 |
+
else
|
| 76 |
+
echo "✅ SUCCESS! $FOUND_COUNT APK(s) moved to: $TARGET_DIR"
|
| 77 |
+
fi
|
public/scripts/LiveSync.js
CHANGED
|
@@ -209,6 +209,16 @@ export class LiveSyncClient {
|
|
| 209 |
try {
|
| 210 |
if (typeof event.data === 'string') {
|
| 211 |
const msg = JSON.parse(event.data);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 212 |
this._handleYjsMessage(msg);
|
| 213 |
} else if (event.data instanceof Blob) {
|
| 214 |
// Binary message - could be Yjs update
|
|
@@ -307,6 +317,15 @@ export class LiveSyncClient {
|
|
| 307 |
}
|
| 308 |
};
|
| 309 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
this._yjsSocket.send(JSON.stringify(msg));
|
| 311 |
console.log(`[Yjs] Sent state update for page ${this.app.state.idx}: ${msg.history.length} strokes`);
|
| 312 |
}
|
|
@@ -2173,21 +2192,40 @@ export class LiveSyncClient {
|
|
| 2173 |
* - Deletions: respect deletions (deleted items stay deleted)
|
| 2174 |
*/
|
| 2175 |
_crdtMergeHistory(localHistory, remoteHistory, remoteTimestamp) {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2176 |
// Build a map keyed by stroke ID for efficient merging
|
| 2177 |
const mergedMap = new Map();
|
|
|
|
| 2178 |
|
| 2179 |
// Add all local items first
|
| 2180 |
for (const item of localHistory) {
|
| 2181 |
-
|
| 2182 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2183 |
}
|
| 2184 |
}
|
| 2185 |
|
| 2186 |
// Merge remote items with CRDT rules
|
| 2187 |
for (const remoteItem of remoteHistory) {
|
| 2188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2189 |
|
| 2190 |
-
const localItem = mergedMap.get(
|
| 2191 |
|
| 2192 |
if (!localItem) {
|
| 2193 |
// New item from remote - add it
|
|
@@ -2223,8 +2261,10 @@ export class LiveSyncClient {
|
|
| 2223 |
result.sort((a, b) => {
|
| 2224 |
// Extract timestamp from ID if possible (e.g., "stroke_1234567890_xyz")
|
| 2225 |
const getCreationTime = (item) => {
|
| 2226 |
-
|
| 2227 |
-
|
|
|
|
|
|
|
| 2228 |
if (parts.length >= 2) {
|
| 2229 |
const ts = parseInt(parts[1]);
|
| 2230 |
if (!isNaN(ts)) return ts;
|
|
|
|
| 209 |
try {
|
| 210 |
if (typeof event.data === 'string') {
|
| 211 |
const msg = JSON.parse(event.data);
|
| 212 |
+
// DEBUG: Log received message structure
|
| 213 |
+
if (msg.type === 'state-update') {
|
| 214 |
+
console.log('[Yjs Debug] Received state-update:', {
|
| 215 |
+
pageIdx: msg.pageIdx,
|
| 216 |
+
historyLength: msg.history?.length || 0,
|
| 217 |
+
firstItemId: msg.history?.[0]?.id,
|
| 218 |
+
firstItemIdType: typeof msg.history?.[0]?.id,
|
| 219 |
+
sampleItem: msg.history?.[0]
|
| 220 |
+
});
|
| 221 |
+
}
|
| 222 |
this._handleYjsMessage(msg);
|
| 223 |
} else if (event.data instanceof Blob) {
|
| 224 |
// Binary message - could be Yjs update
|
|
|
|
| 317 |
}
|
| 318 |
};
|
| 319 |
|
| 320 |
+
// DEBUG: Log what we're sending
|
| 321 |
+
console.log('[Yjs Debug] Sending state-update:', {
|
| 322 |
+
pageIdx: msg.pageIdx,
|
| 323 |
+
historyLength: msg.history.length,
|
| 324 |
+
firstItemId: msg.history[0]?.id,
|
| 325 |
+
firstItemIdType: typeof msg.history[0]?.id,
|
| 326 |
+
sampleItem: msg.history[0]
|
| 327 |
+
});
|
| 328 |
+
|
| 329 |
this._yjsSocket.send(JSON.stringify(msg));
|
| 330 |
console.log(`[Yjs] Sent state update for page ${this.app.state.idx}: ${msg.history.length} strokes`);
|
| 331 |
}
|
|
|
|
| 2192 |
* - Deletions: respect deletions (deleted items stay deleted)
|
| 2193 |
*/
|
| 2194 |
_crdtMergeHistory(localHistory, remoteHistory, remoteTimestamp) {
|
| 2195 |
+
// DEBUG: Log input data types
|
| 2196 |
+
console.log('[CRDT Debug] Starting merge:', {
|
| 2197 |
+
localCount: localHistory?.length || 0,
|
| 2198 |
+
remoteCount: remoteHistory?.length || 0,
|
| 2199 |
+
localSample: localHistory?.[0] ? { id: localHistory[0].id, idType: typeof localHistory[0].id } : null,
|
| 2200 |
+
remoteSample: remoteHistory?.[0] ? { id: remoteHistory[0].id, idType: typeof remoteHistory[0].id } : null
|
| 2201 |
+
});
|
| 2202 |
+
|
| 2203 |
// Build a map keyed by stroke ID for efficient merging
|
| 2204 |
const mergedMap = new Map();
|
| 2205 |
+
const itemsWithoutId = []; // Track items without IDs
|
| 2206 |
|
| 2207 |
// Add all local items first
|
| 2208 |
for (const item of localHistory) {
|
| 2209 |
+
const itemId = item.id != null ? String(item.id) : null;
|
| 2210 |
+
if (itemId) {
|
| 2211 |
+
mergedMap.set(itemId, { ...item });
|
| 2212 |
+
} else {
|
| 2213 |
+
// Items without ID - keep them but can't merge
|
| 2214 |
+
itemsWithoutId.push({ ...item });
|
| 2215 |
+
console.warn('[CRDT Debug] Local item has no ID:', item);
|
| 2216 |
}
|
| 2217 |
}
|
| 2218 |
|
| 2219 |
// Merge remote items with CRDT rules
|
| 2220 |
for (const remoteItem of remoteHistory) {
|
| 2221 |
+
const remoteId = remoteItem.id != null ? String(remoteItem.id) : null;
|
| 2222 |
+
if (!remoteId) {
|
| 2223 |
+
// Remote item without ID - skip
|
| 2224 |
+
console.warn('[CRDT Debug] Remote item has no ID:', remoteItem);
|
| 2225 |
+
continue;
|
| 2226 |
+
}
|
| 2227 |
|
| 2228 |
+
const localItem = mergedMap.get(remoteId);
|
| 2229 |
|
| 2230 |
if (!localItem) {
|
| 2231 |
// New item from remote - add it
|
|
|
|
| 2261 |
result.sort((a, b) => {
|
| 2262 |
// Extract timestamp from ID if possible (e.g., "stroke_1234567890_xyz")
|
| 2263 |
const getCreationTime = (item) => {
|
| 2264 |
+
// Ensure id is a string before calling string methods
|
| 2265 |
+
const id = item.id;
|
| 2266 |
+
if (id && typeof id === 'string' && id.includes('_')) {
|
| 2267 |
+
const parts = id.split('_');
|
| 2268 |
if (parts.length >= 2) {
|
| 2269 |
const ts = parseInt(parts[1]);
|
| 2270 |
if (!isNaN(ts)) return ts;
|