Jaimodiji commited on
Commit
f751881
·
verified ·
1 Parent(s): b6e15a8

Upload folder using huggingface_hub

Browse files
android/app/release/baselineProfiles/0/app-release.dm ADDED
Binary file (3.06 kB). View file
 
android/app/release/baselineProfiles/1/app-release.dm ADDED
Binary file (3.01 kB). View file
 
android/app/release/output-metadata.json ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "version": 3,
3
+ "artifactType": {
4
+ "type": "APK",
5
+ "kind": "Directory"
6
+ },
7
+ "applicationId": "shubham.akshit.tldraw",
8
+ "variantName": "release",
9
+ "elements": [
10
+ {
11
+ "type": "SINGLE",
12
+ "filters": [],
13
+ "attributes": [],
14
+ "versionCode": 1,
15
+ "versionName": "1.0",
16
+ "outputFile": "app-release.apk"
17
+ }
18
+ ],
19
+ "elementType": "File",
20
+ "baselineProfiles": [
21
+ {
22
+ "minApi": 28,
23
+ "maxApi": 30,
24
+ "baselineProfiles": [
25
+ "baselineProfiles/1/app-release.dm"
26
+ ]
27
+ },
28
+ {
29
+ "minApi": 31,
30
+ "maxApi": 2147483647,
31
+ "baselineProfiles": [
32
+ "baselineProfiles/0/app-release.dm"
33
+ ]
34
+ }
35
+ ],
36
+ "minSdkVersionForDexing": 24
37
+ }
client/components/AuthModal.tsx CHANGED
@@ -1,6 +1,7 @@
1
  import { useState } from 'react'
2
  import { useAuth } from '../hooks/useAuth'
3
  import { colors } from '../constants/theme'
 
4
 
5
  interface AuthModalProps {
6
  isOpen: boolean
@@ -25,7 +26,7 @@ export function AuthModal({ isOpen, onClose }: AuthModalProps) {
25
  try {
26
  const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register'
27
 
28
- const res = await fetch(endpoint, {
29
  method: 'POST',
30
  headers: { 'Content-Type': 'application/json' },
31
  body: JSON.stringify({ username, password })
 
1
  import { useState } from 'react'
2
  import { useAuth } from '../hooks/useAuth'
3
  import { colors } from '../constants/theme'
4
+ import { apiUrl } from '../config'
5
 
6
  interface AuthModalProps {
7
  isOpen: boolean
 
26
  try {
27
  const endpoint = isLogin ? '/api/auth/login' : '/api/auth/register'
28
 
29
+ const res = await fetch(apiUrl(endpoint), {
30
  method: 'POST',
31
  headers: { 'Content-Type': 'application/json' },
32
  body: JSON.stringify({ username, password })
client/config.ts CHANGED
@@ -1,6 +1,11 @@
1
  // --- SINGLE SOURCE OF TRUTH ---
2
  const VITE_PREVIEW_URL = import.meta.env.VITE_PREVIEW_URL
3
 
 
 
 
 
 
4
  const VERSION = "1.0.5-" + Date.now();
5
  console.log(`[Config] Version: ${VERSION}`);
6
  console.log(`[Config] window.location:`, {
@@ -11,13 +16,38 @@ console.log(`[Config] window.location:`, {
11
 
12
  let origin = window.location.origin
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  if (VITE_PREVIEW_URL) {
15
  console.log(`[Config] Using VITE_PREVIEW_URL: ${VITE_PREVIEW_URL}`);
16
  origin = VITE_PREVIEW_URL.replace(/\/$/, '')
 
 
 
 
 
17
  }
18
 
19
  // Force WSS if we are on HTTPS
20
  export const SERVER_URL = origin
21
  export const WS_URL = origin.replace(/^http/, 'ws')
22
 
 
 
 
 
 
 
23
  console.log(`[Config] FINAL -> SERVER_URL: ${SERVER_URL}, WS_URL: ${WS_URL}`);
 
1
  // --- SINGLE SOURCE OF TRUTH ---
2
  const VITE_PREVIEW_URL = import.meta.env.VITE_PREVIEW_URL
3
 
4
+ const BACKENDS = {
5
+ hf: 'https://jaimodiji-my-multiplayer-app.hf.space',
6
+ cloudflare: 'https://multiplayer-template.bossemail.workers.dev'
7
+ };
8
+
9
  const VERSION = "1.0.5-" + Date.now();
10
  console.log(`[Config] Version: ${VERSION}`);
11
  console.log(`[Config] window.location:`, {
 
16
 
17
  let origin = window.location.origin
18
 
19
+ // Helper to detect if we are in a "remote" mode (served from a server, not bundled)
20
+ const isRemoteMode = () => {
21
+ const host = window.location.host;
22
+ return host.includes('hf.space') ||
23
+ host.includes('workers.dev') ||
24
+ host.includes('duckdns.org') ||
25
+ host.includes('192.168.');
26
+ };
27
+
28
+ // Helper to detect Capacitor
29
+ const isCapacitor = () => {
30
+ return !!(window as any).Capacitor;
31
+ };
32
+
33
  if (VITE_PREVIEW_URL) {
34
  console.log(`[Config] Using VITE_PREVIEW_URL: ${VITE_PREVIEW_URL}`);
35
  origin = VITE_PREVIEW_URL.replace(/\/$/, '')
36
+ } else if (isCapacitor() && !isRemoteMode()) {
37
+ // We are likely in bundled mode on a device
38
+ const preferredBackend = localStorage.getItem('color_rm_backend') || 'cloudflare';
39
+ origin = (BACKENDS as any)[preferredBackend] || BACKENDS.cloudflare;
40
+ console.log(`[Config] Capacitor bundled mode detected. Using backend: ${origin}`);
41
  }
42
 
43
  // Force WSS if we are on HTTPS
44
  export const SERVER_URL = origin
45
  export const WS_URL = origin.replace(/^http/, 'ws')
46
 
47
+ export const apiUrl = (path: string) => {
48
+ // Ensure path starts with /
49
+ const normalizedPath = path.startsWith('/') ? path : '/' + path;
50
+ return SERVER_URL + normalizedPath;
51
+ }
52
+
53
  console.log(`[Config] FINAL -> SERVER_URL: ${SERVER_URL}, WS_URL: ${WS_URL}`);
client/getBookmarkPreview.tsx CHANGED
@@ -1,4 +1,5 @@
1
  import { AssetRecordType, TLAsset, TLBookmarkAsset, getHashForString } from 'tldraw'
 
2
 
3
  // How does our server handle bookmark unfurling?
4
  export async function getBookmarkPreview({ url }: { url: string }): Promise<TLAsset> {
@@ -19,7 +20,7 @@ export async function getBookmarkPreview({ url }: { url: string }): Promise<TLAs
19
 
20
  try {
21
  // try to fetch the preview data from the server
22
- const response = await fetch(`/api/unfurl?url=${encodeURIComponent(url)}`)
23
  const data: any = await response.json()
24
 
25
  // fill in our asset with whatever info we found
 
1
  import { AssetRecordType, TLAsset, TLBookmarkAsset, getHashForString } from 'tldraw'
2
+ import { apiUrl } from './config'
3
 
4
  // How does our server handle bookmark unfurling?
5
  export async function getBookmarkPreview({ url }: { url: string }): Promise<TLAsset> {
 
20
 
21
  try {
22
  // try to fetch the preview data from the server
23
+ const response = await fetch(apiUrl(`/api/unfurl?url=${encodeURIComponent(url)}`))
24
  const data: any = await response.json()
25
 
26
  // fill in our asset with whatever info we found
client/hooks/useBackups.ts CHANGED
@@ -1,6 +1,6 @@
1
  import { useState, useCallback } from 'react'
2
  import { useAuth } from './useAuth'
3
- import { SERVER_URL } from '../config'
4
 
5
  export interface BackupItem {
6
  key: string
@@ -22,7 +22,7 @@ export function useBackups() {
22
  setIsLoading(true)
23
  setError(null)
24
  try {
25
- const res = await fetch(`${SERVER_URL}/api/backups`, {
26
  headers: { 'Authorization': `Bearer ${token}` }
27
  })
28
  if (!res.ok) throw new Error('Failed to fetch backups')
@@ -39,7 +39,7 @@ export function useBackups() {
39
  if (!isAuthenticated || !token) throw new Error('Not authenticated')
40
 
41
  try {
42
- const res = await fetch(`${SERVER_URL}/api/backup`, {
43
  method: 'POST',
44
  headers: {
45
  'Content-Type': 'application/json',
@@ -62,7 +62,7 @@ export function useBackups() {
62
 
63
  try {
64
  const encodedKey = encodeURIComponent(key)
65
- const res = await fetch(`${SERVER_URL}/api/backup/${encodedKey}`, {
66
  method: 'DELETE',
67
  headers: { 'Authorization': `Bearer ${token}` }
68
  })
@@ -85,7 +85,7 @@ export function useBackups() {
85
  // The server expects encoded key in param: /api/backup/:key
86
  const encodedKey = encodeURIComponent(key)
87
 
88
- const res = await fetch(`${SERVER_URL}/api/backup/${encodedKey}`, {
89
  headers: { 'Authorization': `Bearer ${token}` }
90
  })
91
 
 
1
  import { useState, useCallback } from 'react'
2
  import { useAuth } from './useAuth'
3
+ import { apiUrl } from '../config'
4
 
5
  export interface BackupItem {
6
  key: string
 
22
  setIsLoading(true)
23
  setError(null)
24
  try {
25
+ const res = await fetch(apiUrl('/api/backups'), {
26
  headers: { 'Authorization': `Bearer ${token}` }
27
  })
28
  if (!res.ok) throw new Error('Failed to fetch backups')
 
39
  if (!isAuthenticated || !token) throw new Error('Not authenticated')
40
 
41
  try {
42
+ const res = await fetch(apiUrl('/api/backup'), {
43
  method: 'POST',
44
  headers: {
45
  'Content-Type': 'application/json',
 
62
 
63
  try {
64
  const encodedKey = encodeURIComponent(key)
65
+ const res = await fetch(apiUrl(`/api/backup/${encodedKey}`), {
66
  method: 'DELETE',
67
  headers: { 'Authorization': `Bearer ${token}` }
68
  })
 
85
  // The server expects encoded key in param: /api/backup/:key
86
  const encodedKey = encodeURIComponent(key)
87
 
88
+ const res = await fetch(apiUrl(`/api/backup/${encodedKey}`), {
89
  headers: { 'Authorization': `Bearer ${token}` }
90
  })
91
 
client/multiplayerAssetStore.tsx CHANGED
@@ -1,6 +1,6 @@
1
  import { TLAssetStore, uniqueId } from 'tldraw'
2
  // 1. IMPORT CONFIG
3
- import { SERVER_URL } from './config'
4
 
5
  export const multiplayerAssetStore: TLAssetStore = {
6
  async upload(_asset, file) {
@@ -8,7 +8,7 @@ export const multiplayerAssetStore: TLAssetStore = {
8
  const objectName = `${id}-${file.name}`.replace(/[^a-zA-Z0-9.]/g, '-')
9
 
10
  // 2. USE CONFIG
11
- const url = `${SERVER_URL}/api/uploads/${objectName}`
12
 
13
  const response = await fetch(url, {
14
  method: 'POST',
 
1
  import { TLAssetStore, uniqueId } from 'tldraw'
2
  // 1. IMPORT CONFIG
3
+ import { apiUrl } from './config'
4
 
5
  export const multiplayerAssetStore: TLAssetStore = {
6
  async upload(_asset, file) {
 
8
  const objectName = `${id}-${file.name}`.replace(/[^a-zA-Z0-9.]/g, '-')
9
 
10
  // 2. USE CONFIG
11
+ const url = apiUrl(`/api/uploads/${objectName}`)
12
 
13
  const response = await fetch(url, {
14
  method: 'POST',
client/pages/Lobby.tsx CHANGED
@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react'
2
  import { useNavigate } from 'react-router-dom'
3
  import { uniqueId } from 'tldraw'
4
 
 
5
  import { generateBoardName } from '../utils/nameGenerator'
6
  import { saveRoom } from './storageUtils'
7
  import { useAuth } from '../hooks/useAuth'
@@ -44,7 +45,7 @@ export function Lobby() {
44
 
45
  useEffect(() => {
46
  if (isAuthenticated && token) {
47
- fetch('/api/color_rm/registry', {
48
  headers: {
49
  'Authorization': `Bearer ${token}`
50
  }
 
2
  import { useNavigate } from 'react-router-dom'
3
  import { uniqueId } from 'tldraw'
4
 
5
+ import { apiUrl } from '../config'
6
  import { generateBoardName } from '../utils/nameGenerator'
7
  import { saveRoom } from './storageUtils'
8
  import { useAuth } from '../hooks/useAuth'
 
45
 
46
  useEffect(() => {
47
  if (isAuthenticated && token) {
48
+ fetch(apiUrl('/api/color_rm/registry'), {
49
  headers: {
50
  'Authorization': `Bearer ${token}`
51
  }
client/pages/RoomPage.tsx CHANGED
@@ -18,7 +18,7 @@ import {
18
  import { useSync } from '@tldraw/sync'
19
  import { getBookmarkPreview } from '../getBookmarkPreview'
20
  import { multiplayerAssetStore } from '../multiplayerAssetStore'
21
- import { WS_URL, SERVER_URL } from '../config'
22
  import { saveRoom } from './storageUtils'
23
  import { StatePersistence } from '../components/StatePersistence'
24
  import { NavigationDock } from '../components/NavigationDock'
@@ -150,7 +150,7 @@ export function RoomPage() {
150
 
151
  useEffect(() => {
152
  saveRoom(roomId)
153
- const metaUrl = `${SERVER_URL}/api/meta/${roomId}`;
154
  console.log(`[Room] Fetching metadata from: ${metaUrl}`);
155
 
156
  fetch(metaUrl)
 
18
  import { useSync } from '@tldraw/sync'
19
  import { getBookmarkPreview } from '../getBookmarkPreview'
20
  import { multiplayerAssetStore } from '../multiplayerAssetStore'
21
+ import { WS_URL, SERVER_URL, apiUrl } from '../config'
22
  import { saveRoom } from './storageUtils'
23
  import { StatePersistence } from '../components/StatePersistence'
24
  import { NavigationDock } from '../components/NavigationDock'
 
150
 
151
  useEffect(() => {
152
  saveRoom(roomId)
153
+ const metaUrl = apiUrl(`/api/meta/${roomId}`);
154
  console.log(`[Room] Fetching metadata from: ${metaUrl}`);
155
 
156
  fetch(metaUrl)
public/color_rm.html CHANGED
@@ -384,6 +384,7 @@
384
  </div>
385
  <div class="picker-body">
386
  <div id="iroWheel"></div>
 
387
  <button id="pickerActionBtn" class="btn btn-primary" style="width:100%; margin-top:10px;">Set</button>
388
  <button id="pickerNoneBtn" class="btn" style="width:100%; margin-top:5px; display:none">Transparent</button>
389
  </div>
 
384
  </div>
385
  <div class="picker-body">
386
  <div id="iroWheel"></div>
387
+ <div id="pickerSwatches" style="display:flex; gap:6px; margin-top:10px; flex-wrap:wrap; justify-content:center; width:100%;"></div>
388
  <button id="pickerActionBtn" class="btn btn-primary" style="width:100%; margin-top:10px;">Set</button>
389
  <button id="pickerNoneBtn" class="btn" style="width:100%; margin-top:5px; display:none">Transparent</button>
390
  </div>
public/scripts/ColorRmApp.js CHANGED
@@ -13,7 +13,8 @@ export class ColorRmApp {
13
 
14
  this.state = {
15
  sessionId: null, images: [], idx: 0,
16
- colors: [], strict: 15, tool: 'none', bg: 'transparent',
 
17
  penColor: '#ef4444', penSize: 3, eraserSize: 20, eraserType: 'stroke',
18
  textSize: 40,
19
  shapeType: 'rectangle', shapeBorder: '#3b82f6', shapeFill: 'transparent', shapeWidth: 3,
@@ -534,8 +535,15 @@ export class ColorRmApp {
534
  const pickerAction = this.getElement('pickerActionBtn');
535
  if(pickerAction) {
536
  pickerAction.onclick = () => {
 
 
 
 
 
 
 
 
537
  if(this.state.pickerMode==='remove') {
538
- const hex = this.iroP.color.hexString;
539
  const i = parseInt(hex.slice(1), 16);
540
  this.state.colors.push({hex, lab:this.rgbToLab((i>>16)&255,(i>>8)&255,i&255)});
541
  this.renderSwatches();
@@ -1231,6 +1239,7 @@ export class ColorRmApp {
1231
  this.state.pickerMode=m;
1232
  const pb = this.getElement('pickerNoneBtn');
1233
  if(pb) pb.style.display = (m==='shapeFill'||m==='selectionFill') ? 'block' : 'none';
 
1234
  const fp = this.getElement('floatingPicker');
1235
  if(fp) fp.style.display='flex';
1236
  }
@@ -1486,6 +1495,22 @@ export class ColorRmApp {
1486
  });
1487
  }
1488
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1489
  switchSideTab(tab) {
1490
  this.state.activeSideTab = tab;
1491
  const tabs = ['tools', 'pages', 'box', 'debug'];
 
13
 
14
  this.state = {
15
  sessionId: null, images: [], idx: 0,
16
+ colors: [], customSwatches: JSON.parse(localStorage.getItem('crm_custom_colors') || '[]'),
17
+ strict: 15, tool: 'none', bg: 'transparent',
18
  penColor: '#ef4444', penSize: 3, eraserSize: 20, eraserType: 'stroke',
19
  textSize: 40,
20
  shapeType: 'rectangle', shapeBorder: '#3b82f6', shapeFill: 'transparent', shapeWidth: 3,
 
535
  const pickerAction = this.getElement('pickerActionBtn');
536
  if(pickerAction) {
537
  pickerAction.onclick = () => {
538
+ const hex = this.iroP.color.hexString;
539
+
540
+ // Save to custom swatches history (max 14)
541
+ this.state.customSwatches = this.state.customSwatches.filter(c => c !== hex);
542
+ this.state.customSwatches.unshift(hex);
543
+ if(this.state.customSwatches.length > 14) this.state.customSwatches.pop();
544
+ localStorage.setItem('crm_custom_colors', JSON.stringify(this.state.customSwatches));
545
+
546
  if(this.state.pickerMode==='remove') {
 
547
  const i = parseInt(hex.slice(1), 16);
548
  this.state.colors.push({hex, lab:this.rgbToLab((i>>16)&255,(i>>8)&255,i&255)});
549
  this.renderSwatches();
 
1239
  this.state.pickerMode=m;
1240
  const pb = this.getElement('pickerNoneBtn');
1241
  if(pb) pb.style.display = (m==='shapeFill'||m==='selectionFill') ? 'block' : 'none';
1242
+ this.renderPickerSwatches();
1243
  const fp = this.getElement('floatingPicker');
1244
  if(fp) fp.style.display='flex';
1245
  }
 
1495
  });
1496
  }
1497
 
1498
+ renderPickerSwatches() {
1499
+ const c = this.getElement('pickerSwatches');
1500
+ if (!c) return;
1501
+ c.innerHTML = '';
1502
+ this.state.customSwatches.forEach(color => {
1503
+ const d = document.createElement('div');
1504
+ d.style.width = '24px'; d.style.height = '24px';
1505
+ d.style.borderRadius = '4px'; d.style.border = '1px solid #333';
1506
+ d.style.cursor = 'pointer'; d.style.background = color;
1507
+ d.onclick = () => {
1508
+ if (this.iroP) this.iroP.color.set(color);
1509
+ };
1510
+ c.appendChild(d);
1511
+ });
1512
+ }
1513
+
1514
  switchSideTab(tab) {
1515
  this.state.activeSideTab = tab;
1516
  const tabs = ['tools', 'pages', 'box', 'debug'];