shraga 0.1.103 → 0.1.104

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -13,7 +13,7 @@
13
13
  <link rel="preconnect" href="https://fonts.googleapis.com" />
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
15
15
  <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap" rel="stylesheet" />
16
- <script type="module" crossorigin src="/assets/index-OVPvoXiu.js"></script>
16
+ <script type="module" crossorigin src="/assets/index-DSNBsFVU.js"></script>
17
17
  <link rel="stylesheet" crossorigin href="/assets/index-CibL2gLy.css">
18
18
  </head>
19
19
  <body>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shraga",
3
- "version": "0.1.103",
3
+ "version": "0.1.104",
4
4
  "description": "The teammate you delegate coding to — a self-hostable, multi-user AI coding agent web UI (Claude Code, with a pluggable engine seam).",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -169,6 +169,7 @@ function AppInner() {
169
169
  }
170
170
  }, [bumpSidebar]);
171
171
 
172
+ const workspaceRefreshTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
172
173
  const markReadRef = useRef<((sid: string) => void) | null>(null);
173
174
  const unreadEventRef = useRef<((event: ServerEvent) => void) | null>(null);
174
175
  const activeSessionRef = useRef(activeSessionId);
@@ -186,7 +187,13 @@ function AppInner() {
186
187
  if (event.type === 'permission_request') requestDesktopAttention(true);
187
188
  switch (event.type) {
188
189
  case 'workspace_change':
189
- refreshWorkspace();
190
+ // A burst of changes (one event per path) collapses into a single tree refetch.
191
+ if (!workspaceRefreshTimer.current) {
192
+ workspaceRefreshTimer.current = setTimeout(() => {
193
+ workspaceRefreshTimer.current = null;
194
+ refreshWorkspace();
195
+ }, 1000);
196
+ }
190
197
  break;
191
198
  case 'session_title_updated':
192
199
  bumpSidebar();
@@ -227,6 +227,9 @@ export function watchWorkspace(cb: (event: { action: 'created' | 'modified' | 'd
227
227
  pending.clear();
228
228
  timer = null;
229
229
  };
230
+ // Last seen mtime:size per path. Bun's recursive watcher can get stuck re-emitting an event for an
231
+ // unchanged file (observed: ~14/s for hours) — each one fans out to a full tree refetch per client.
232
+ const seen = new Map<string, string>();
230
233
  let watcher: import('node:fs').FSWatcher;
231
234
  try {
232
235
  watcher = watch(WORKSPACE_DIR, { recursive: true }, (eventType, filename) => {
@@ -234,7 +237,11 @@ export function watchWorkspace(cb: (event: { action: 'created' | 'modified' | 'd
234
237
  const rel = String(filename).split(path.sep).join('/');
235
238
  if (rel.split('/').some((seg) => seg.startsWith('.'))) return;
236
239
  const full = path.join(WORKSPACE_DIR, rel);
237
- const action: 'created' | 'modified' | 'deleted' = existsSync(full)
240
+ let sig: string | undefined;
241
+ try { const st = statSync(full); sig = `${st.mtimeMs}:${st.size}`; } catch {}
242
+ if (sig !== undefined && seen.get(rel) === sig) return;
243
+ if (sig === undefined) seen.delete(rel); else seen.set(rel, sig);
244
+ const action: 'created' | 'modified' | 'deleted' = sig !== undefined
238
245
  ? (eventType === 'rename' ? 'created' : 'modified')
239
246
  : 'deleted';
240
247
  pending.set(rel, action);