@unbrained/pm-web 2026.9.18 → 2026.9.26

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.
Files changed (126) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +1 -4
  3. package/dist/app.d.ts +10 -0
  4. package/dist/app.js +19 -9
  5. package/dist/app.js.map +1 -1
  6. package/dist/index.js +1 -1
  7. package/dist/routes/admin.js +6 -6
  8. package/dist/routes/admin.js.map +1 -1
  9. package/dist/routes/auth.js +2 -2
  10. package/dist/routes/auth.js.map +1 -1
  11. package/dist/routes/extensions.js +24 -37
  12. package/dist/routes/extensions.js.map +1 -1
  13. package/dist/routes/github.js +169 -107
  14. package/dist/routes/github.js.map +1 -1
  15. package/dist/routes/groups.js +9 -14
  16. package/dist/routes/groups.js.map +1 -1
  17. package/dist/routes/pm.js +532 -1015
  18. package/dist/routes/pm.js.map +1 -1
  19. package/dist/routes/projects.js +10 -6
  20. package/dist/routes/projects.js.map +1 -1
  21. package/dist/routes/route-helpers.d.ts +74 -0
  22. package/dist/routes/route-helpers.js +85 -0
  23. package/dist/routes/route-helpers.js.map +1 -0
  24. package/dist/routes/sharing.js +30 -18
  25. package/dist/routes/sharing.js.map +1 -1
  26. package/dist/server.js +1 -20
  27. package/dist/server.js.map +1 -1
  28. package/dist/services/mutation-event-watcher.js +3 -28
  29. package/dist/services/mutation-event-watcher.js.map +1 -1
  30. package/dist/services/pm-runner.js +3 -1
  31. package/dist/services/pm-runner.js.map +1 -1
  32. package/dist/services/project-watcher.js +14 -38
  33. package/dist/services/project-watcher.js.map +1 -1
  34. package/dist/services/watcher-utils.d.ts +42 -0
  35. package/dist/services/watcher-utils.js +60 -0
  36. package/dist/services/watcher-utils.js.map +1 -0
  37. package/manifest.json +2 -2
  38. package/package.json +17 -6
  39. package/public/src/app.js +22 -52
  40. package/public/src/app.js.map +1 -1
  41. package/public/src/app.ts +18 -75
  42. package/public/src/browser-window.js +29 -0
  43. package/public/src/browser-window.js.map +1 -0
  44. package/public/src/browser-window.ts +56 -0
  45. package/public/src/components/modals.js +1 -1
  46. package/public/src/components/modals.js.map +1 -1
  47. package/public/src/components/modals.ts +1 -1
  48. package/public/src/i18n.js +14 -6
  49. package/public/src/i18n.js.map +1 -1
  50. package/public/src/i18n.ts +16 -6
  51. package/public/src/sw.ts +1 -0
  52. package/public/src/utils.js +58 -0
  53. package/public/src/utils.js.map +1 -1
  54. package/public/src/utils.ts +53 -0
  55. package/public/src/views/admin.js +2 -5
  56. package/public/src/views/admin.js.map +1 -1
  57. package/public/src/views/admin.ts +2 -2
  58. package/public/src/views/auth.js +5 -1
  59. package/public/src/views/auth.js.map +1 -1
  60. package/public/src/views/auth.ts +6 -1
  61. package/public/src/views/calendar.js +1 -2
  62. package/public/src/views/calendar.js.map +1 -1
  63. package/public/src/views/calendar.ts +1 -2
  64. package/public/src/views/config.js +14 -21
  65. package/public/src/views/config.js.map +1 -1
  66. package/public/src/views/config.ts +14 -19
  67. package/public/src/views/context.js +11 -12
  68. package/public/src/views/context.js.map +1 -1
  69. package/public/src/views/context.ts +13 -12
  70. package/public/src/views/create.js +3 -11
  71. package/public/src/views/create.js.map +1 -1
  72. package/public/src/views/create.ts +3 -3
  73. package/public/src/views/export.js +2 -3
  74. package/public/src/views/export.js.map +1 -1
  75. package/public/src/views/export.ts +2 -3
  76. package/public/src/views/github.js +33 -47
  77. package/public/src/views/github.js.map +1 -1
  78. package/public/src/views/github.ts +35 -28
  79. package/public/src/views/graph-canvas.js +92 -99
  80. package/public/src/views/graph-canvas.js.map +1 -1
  81. package/public/src/views/graph-canvas.ts +94 -93
  82. package/public/src/views/graph.js +88 -73
  83. package/public/src/views/graph.js.map +1 -1
  84. package/public/src/views/graph.ts +97 -63
  85. package/public/src/views/groups.js +2 -5
  86. package/public/src/views/groups.js.map +1 -1
  87. package/public/src/views/groups.ts +2 -2
  88. package/public/src/views/guide.js +11 -10
  89. package/public/src/views/guide.js.map +1 -1
  90. package/public/src/views/guide.ts +12 -10
  91. package/public/src/views/items.js +100 -130
  92. package/public/src/views/items.js.map +1 -1
  93. package/public/src/views/items.ts +99 -117
  94. package/public/src/views/normalize.js +2 -9
  95. package/public/src/views/normalize.js.map +1 -1
  96. package/public/src/views/normalize.ts +2 -9
  97. package/public/src/views/packages.js +7 -4
  98. package/public/src/views/packages.js.map +1 -1
  99. package/public/src/views/packages.ts +8 -4
  100. package/public/src/views/plan-execution.js.map +1 -1
  101. package/public/src/views/plan-execution.ts +5 -15
  102. package/public/src/views/plan.js +2 -1
  103. package/public/src/views/plan.js.map +1 -1
  104. package/public/src/views/plan.ts +6 -17
  105. package/public/src/views/projects.js +5 -6
  106. package/public/src/views/projects.js.map +1 -1
  107. package/public/src/views/projects.ts +5 -6
  108. package/public/src/views/search.js +5 -1
  109. package/public/src/views/search.js.map +1 -1
  110. package/public/src/views/search.ts +7 -1
  111. package/public/src/views/settings.js +30 -55
  112. package/public/src/views/settings.js.map +1 -1
  113. package/public/src/views/settings.ts +23 -17
  114. package/public/src/views/shared.js +2 -5
  115. package/public/src/views/shared.js.map +1 -1
  116. package/public/src/views/shared.ts +2 -5
  117. package/public/src/views/sharing.js +2 -5
  118. package/public/src/views/sharing.js.map +1 -1
  119. package/public/src/views/sharing.ts +2 -2
  120. package/public/src/views/templates.js +9 -16
  121. package/public/src/views/templates.js.map +1 -1
  122. package/public/src/views/templates.ts +9 -14
  123. package/public/src/views/validate.js +2 -9
  124. package/public/src/views/validate.js.map +1 -1
  125. package/public/src/views/validate.ts +2 -9
  126. package/public/sw.js +1 -0
package/public/src/app.ts CHANGED
@@ -4,16 +4,16 @@
4
4
  import { state } from './state.js';
5
5
  import { api, csrfTokenFromCookie } from './api.js';
6
6
  import type { AuthMeResponse, SearchResponse } from './api-types.js';
7
- import { showView, setOnViewChange } from './views/router.js';
7
+ import { showView, setOnViewChange, getViewForPath } from './views/router.js';
8
8
  import { loadProjects, onProjectSelect, loadItemsBadge, renderProjectsView, selectProject, deleteProject, buildCreateProjectModal, submitCreateProject, submitCreateProject2 } from './views/projects.js';
9
- import { renderItemsView, fetchAndRenderItems, openItemDetail, switchDetailTab, addComment, addNote, appendItem, updateItem, closeItem, confirmDeleteItem, claimItem, releaseItem, startItem, pauseItem, addDep, removeDep, addLearning, addTest, addFileLink, setStatusFilter, applyItemFilters, clearFilters, copyFilterLink, showBulkUpdateModal, previewBulkUpdate, applyBulkUpdate, showBulkCloseModal, previewBulkClose, applyBulkClose, useItemAsTemplate } from './views/items.js';
9
+ import * as itemsView from './views/items.js';
10
10
  import { submitCreateItem, submitCreateItemAndOpen } from './views/create.js';
11
11
  import { renderActivityView } from './views/activity.js';
12
12
  import { renderSearchView, setSearchMode, reindexProject, debouncedSearch, doSearch } from './views/search.js';
13
13
  import { renderStatsView } from './views/stats.js';
14
14
  import { renderCalendarView, calNav, showDayItems } from './views/calendar.js';
15
15
  import { renderContextView } from './views/context.js';
16
- import { renderGraphView } from './views/graph.js';
16
+ import { renderGraphView, refreshGraphData as refreshGraphViewData } from './views/graph.js';
17
17
 
18
18
  /** Switch to the graph view, wait for it to mount, render it, then focus the
19
19
  * node with the given id by invoking the graph view's select-node hook. */
@@ -23,7 +23,7 @@ async function openGraphAt(nodeId: string): Promise<void> {
23
23
  setTimeout(async () => {
24
24
  await renderGraphView();
25
25
  // Select the node via the graph canvas
26
- window.__graphSelectNode?.(nodeId);
26
+ browserWindow().__graphSelectNode?.(nodeId);
27
27
  }, 50);
28
28
  }
29
29
  import { renderSharingView, openShareModal, submitShare, removeShare } from './views/sharing.js';
@@ -47,7 +47,8 @@ import { initI18n, setLocale as i18nSetLocale, applyTranslations } from './i18n.
47
47
  import { initPlanView, openPlanDetail, openCreatePlanModal, submitCreatePlan, openAddStepModal, submitAddStep, planCompleteStep, planBlockStepPrompt, submitBlockStep, planRemoveStep, planApprove, planMaterializePrompt, submitMaterializePlan, copyPlanAgentBrief, copyPlanNextStepPrompt, planEditPrompt, submitEditPlan, planDeletePrompt } from './views/plan.js';
48
48
  import { showModal, hideModal, createModal, closeAllModals } from './components/modals.js';
49
49
  import { toast } from './components/toast.js';
50
- import { escHtml } from './utils.js';
50
+ import { escHtml, typeIcon, priorityDot, statusBadge, updateHeaderUser } from './utils.js';
51
+ import { browserWindow, isBeforeInstallPromptEvent, type BeforeInstallPromptEvent } from './browser-window.js';
51
52
  import { initTheme, cycleTheme } from './theme.js';
52
53
 
53
54
  // Apply the persisted theme immediately on script load (before boot) so the UI
@@ -195,7 +196,6 @@ async function doGlobalSearch(): Promise<void> {
195
196
  try {
196
197
  const data = await api<SearchResponse>('POST',`/projects/${state.currentProject.id}/pm/search`,{query});
197
198
  const results = data.results || data.items || [];
198
- const { escHtml, typeIcon, priorityDot, statusBadge } = await import('./utils.js');
199
199
  if (resultsEl) resultsEl.innerHTML = results.length === 0
200
200
  ? `<div class="empty-state" style="padding:24px"><div class="empty-state-text">No results for "${escHtml(query)}"</div></div>`
201
201
  : `<div class="item-list">${results.map((item) =>`
@@ -213,10 +213,6 @@ async function doGlobalSearch(): Promise<void> {
213
213
  // PWA install
214
214
  // The DOM lib has no `BeforeInstallPromptEvent` type, so model the two members
215
215
  // the install flow actually uses (`prompt()` and `userChoice`).
216
- interface BeforeInstallPromptEvent extends Event {
217
- prompt(): Promise<void>;
218
- readonly userChoice: Promise<{ outcome: 'accepted' | 'dismissed'; platform: string }>;
219
- }
220
216
 
221
217
  let deferredPrompt: BeforeInstallPromptEvent | null = null;
222
218
 
@@ -226,7 +222,6 @@ const __app = {
226
222
  // Views
227
223
  showView,
228
224
  renderProjectsView,
229
- renderItemsView,
230
225
  renderActivityView,
231
226
  renderSearchView,
232
227
  renderStatsView,
@@ -274,34 +269,7 @@ const __app = {
274
269
  submitCreateProject2,
275
270
 
276
271
  // Items
277
- openItemDetail,
278
- switchDetailTab,
279
- addComment,
280
- addNote,
281
- appendItem,
282
- updateItem,
283
- closeItem,
284
- confirmDeleteItem,
285
- claimItem,
286
- releaseItem,
287
- startItem,
288
- pauseItem,
289
- addDep,
290
- removeDep,
291
- addLearning,
292
- addTest,
293
- addFileLink,
294
- setStatusFilter,
295
- applyItemFilters,
296
- clearFilters,
297
- copyFilterLink,
298
- showBulkUpdateModal,
299
- previewBulkUpdate,
300
- applyBulkUpdate,
301
- showBulkCloseModal,
302
- previewBulkClose,
303
- applyBulkClose,
304
- useItemAsTemplate,
272
+ ...itemsView,
305
273
 
306
274
  // Create
307
275
  submitCreateItem,
@@ -421,28 +389,7 @@ const __app = {
421
389
  * handlers reach through `window.__app` is enumerated here via `typeof __app`. */
422
390
  export type AppBridge = typeof __app;
423
391
 
424
- // Global augmentation: type the `window.__app` bridge plus the two PWA install
425
- // helpers assigned below. `beforeinstallprompt` is augmented on WindowEventMap
426
- // so the listener receives our `BeforeInstallPromptEvent`.
427
- declare global {
428
- interface Window {
429
- __app?: AppBridge;
430
- installPwa?: () => void;
431
- dismissInstallBanner?: () => void;
432
- /**
433
- * Graph view's keydown handler, parked on `window` so the view can detach
434
- * the exact same function reference when it tears down.
435
- */
436
- __graphKeyHandler?: (e: KeyboardEvent) => void;
437
- /** Set by the graph view so other views can focus a node by item id. */
438
- __graphSelectNode?: (id: string) => void;
439
- }
440
- interface WindowEventMap {
441
- beforeinstallprompt: BeforeInstallPromptEvent;
442
- }
443
- }
444
-
445
- window.__app = __app;
392
+ browserWindow().__app = __app;
446
393
 
447
394
  // ═══════════════════════════════════════════════════════════════
448
395
  // SSE REAL-TIME SYNC
@@ -594,7 +541,7 @@ function connectSSE(projectId: string, attempt = 0): void {
594
541
  const doRefreshView = () => {
595
542
  const view = state.currentView;
596
543
  if (view === 'items') {
597
- fetchAndRenderItems();
544
+ itemsView.fetchAndRenderItems();
598
545
  } else if (view === 'activity') {
599
546
  renderActivityView();
600
547
  } else if (view === 'stats') {
@@ -613,7 +560,7 @@ function connectSSE(projectId: string, attempt = 0): void {
613
560
  // Graph-synced events (Neo4j sync complete) do a full graph reload
614
561
  const refreshGraph = () => {
615
562
  if (state.currentView === 'graph') {
616
- import('./views/graph.js').then((module) => module.renderGraphView());
563
+ void renderGraphView();
617
564
  }
618
565
  };
619
566
 
@@ -639,7 +586,7 @@ function connectSSE(projectId: string, attempt = 0): void {
639
586
  } catch { /* ignore parse errors */ }
640
587
  }
641
588
  if (state.currentView === 'graph') {
642
- import('./views/graph.js').then((module) => module.refreshGraphData());
589
+ void refreshGraphViewData();
643
590
  } else {
644
591
  refreshView();
645
592
  }
@@ -716,11 +663,7 @@ export async function bootApp(): Promise<void> {
716
663
  if (mainApp) mainApp.style.display = 'flex';
717
664
 
718
665
  const u = state.user!;
719
- const initials = (u.display_name||u.email||'?').split(' ').map(w=>w[0]).join('').slice(0,2).toUpperCase();
720
- const avatarEl = document.getElementById('user-avatar');
721
- if (avatarEl) avatarEl.textContent = initials;
722
- const nameEl = document.getElementById('user-name-display');
723
- if (nameEl) nameEl.textContent = u.display_name||u.email;
666
+ updateHeaderUser(u);
724
667
  document.querySelectorAll<HTMLElement>('.admin-only').forEach((el) => {
725
668
  el.style.display = u.is_admin ? '' : 'none';
726
669
  });
@@ -766,7 +709,6 @@ async function handleLaunchAction(): Promise<void> {
766
709
  }
767
710
 
768
711
  // Restore view from URL path (supports refresh/bookmark)
769
- const { getViewForPath } = await import('./views/router.js');
770
712
  const view = getViewForPath(window.location.pathname);
771
713
 
772
714
  // If view requires a project and none is selected, try to select first one
@@ -945,9 +887,10 @@ document.addEventListener('keydown', e => {
945
887
  // ═══════════════════════════════════════════════════════════════
946
888
  // PWA INSTALL PROMPT
947
889
  // ═══════════════════════════════════════════════════════════════
948
- window.addEventListener('beforeinstallprompt', (e) => {
949
- e.preventDefault();
950
- deferredPrompt = e;
890
+ window.addEventListener('beforeinstallprompt', (event) => {
891
+ if (!isBeforeInstallPromptEvent(event)) return;
892
+ event.preventDefault();
893
+ deferredPrompt = event;
951
894
  const banner = document.getElementById('install-banner');
952
895
  if (banner && !localStorage.getItem('pm-web-install-dismissed')) {
953
896
  banner.classList.add('visible');
@@ -961,7 +904,7 @@ window.addEventListener('appinstalled', () => {
961
904
  toast('pm-web installed!', 'success');
962
905
  });
963
906
 
964
- window.installPwa = function(): void {
907
+ browserWindow().installPwa = function(): void {
965
908
  if (!deferredPrompt) return;
966
909
  deferredPrompt.prompt();
967
910
  deferredPrompt.userChoice.then((result) => {
@@ -974,7 +917,7 @@ window.installPwa = function(): void {
974
917
  });
975
918
  };
976
919
 
977
- window.dismissInstallBanner = function(): void {
920
+ browserWindow().dismissInstallBanner = function(): void {
978
921
  localStorage.setItem('pm-web-install-dismissed', '1');
979
922
  const banner = document.getElementById('install-banner');
980
923
  if (banner) banner.classList.remove('visible');
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Typed view of the window properties this SPA attaches at runtime.
3
+ *
4
+ * Inline onclick handlers still reach `window.__app`. TypeScript call sites
5
+ * use this accessor so the attachment does not need a global namespace.
6
+ */
7
+ /**
8
+ * Return the current window as the augmented browser window.
9
+ *
10
+ * The extra properties are optional and written by this package; a missing
11
+ * window is a non-browser host and must fail loudly rather than pretend the
12
+ * bridge exists.
13
+ */
14
+ export function browserWindow() {
15
+ if (typeof window === "undefined") {
16
+ throw new Error("browser window is unavailable");
17
+ }
18
+ return window;
19
+ }
20
+ /**
21
+ * Whether a DOM event is the install prompt this package listens for.
22
+ *
23
+ * @param event - The event delivered to a `beforeinstallprompt` listener.
24
+ * @returns True when the event exposes `prompt` and `userChoice`.
25
+ */
26
+ export function isBeforeInstallPromptEvent(event) {
27
+ return "prompt" in event && "userChoice" in event;
28
+ }
29
+ //# sourceMappingURL=browser-window.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-window.js","sourceRoot":"","sources":["browser-window.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA4BH;;;;;;GAMG;AACH,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,+BAA+B,CAAC,CAAC;IACnD,CAAC;IACD,OAAO,MAAuB,CAAC;AACjC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CAAC,KAAY;IACrD,OAAO,QAAQ,IAAI,KAAK,IAAI,YAAY,IAAI,KAAK,CAAC;AACpD,CAAC"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Typed view of the window properties this SPA attaches at runtime.
3
+ *
4
+ * Inline onclick handlers still reach `window.__app`. TypeScript call sites
5
+ * use this accessor so the attachment does not need a global namespace.
6
+ */
7
+
8
+ /** Methods other modules call through the window bridge. */
9
+ export interface AppBridge {
10
+ openItemDetail(id: string): void;
11
+ showView(view: string, pushState?: boolean): void;
12
+ toast(message: string, type?: string): void;
13
+ disconnectSSE(): void;
14
+ connectSSE(projectId: string, attempt?: number): void;
15
+ loadItemsBadge(): void;
16
+ hideModal(id: string): void;
17
+ }
18
+
19
+ /** Window plus the properties this package writes onto it. */
20
+ export interface BrowserWindow extends Window {
21
+ __app?: AppBridge;
22
+ installPwa?: () => void;
23
+ dismissInstallBanner?: () => void;
24
+ __graphKeyHandler?: (event: KeyboardEvent) => void;
25
+ __graphSelectNode?: (id: string) => void;
26
+ }
27
+
28
+ /** The browser `beforeinstallprompt` event, which the DOM lib does not declare. */
29
+ export interface BeforeInstallPromptEvent extends Event {
30
+ prompt(): Promise<void>;
31
+ readonly userChoice: Promise<{ outcome: "accepted" | "dismissed"; platform: string }>;
32
+ }
33
+
34
+ /**
35
+ * Return the current window as the augmented browser window.
36
+ *
37
+ * The extra properties are optional and written by this package; a missing
38
+ * window is a non-browser host and must fail loudly rather than pretend the
39
+ * bridge exists.
40
+ */
41
+ export function browserWindow(): BrowserWindow {
42
+ if (typeof window === "undefined") {
43
+ throw new Error("browser window is unavailable");
44
+ }
45
+ return window as BrowserWindow;
46
+ }
47
+
48
+ /**
49
+ * Whether a DOM event is the install prompt this package listens for.
50
+ *
51
+ * @param event - The event delivered to a `beforeinstallprompt` listener.
52
+ * @returns True when the event exposes `prompt` and `userChoice`.
53
+ */
54
+ export function isBeforeInstallPromptEvent(event: Event): event is BeforeInstallPromptEvent {
55
+ return "prompt" in event && "userChoice" in event;
56
+ }
@@ -25,7 +25,7 @@ export function closeAllModals() {
25
25
  * optional footer markup, append it under `#modal-container`, and wire its
26
26
  * backdrop-click-to-close behavior. Returns the created modal element. */
27
27
  export function createModal(id, title, bodyHtml, footerHtml, wide = false) {
28
- let existing = document.getElementById(id);
28
+ const existing = document.getElementById(id);
29
29
  if (existing)
30
30
  existing.remove();
31
31
  const el = document.createElement('div');
@@ -1 +1 @@
1
- {"version":3,"file":"modals.js","sourceRoot":"","sources":["modals.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"modals.js","sourceRoot":"","sources":["modals.ts"],"names":[],"mappings":"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"}
@@ -33,7 +33,7 @@ export function createModal(
33
33
  footerHtml: string,
34
34
  wide = false
35
35
  ): HTMLElement {
36
- let existing = document.getElementById(id);
36
+ const existing = document.getElementById(id);
37
37
  if (existing) existing.remove();
38
38
  const el = document.createElement('div');
39
39
  el.id = id;
@@ -41,7 +41,7 @@ export function resolveLocale(opts) {
41
41
  // wants that value: `null` skips the step. A property that is absent falls
42
42
  // back to the real browser global, so the no-argument browser path still
43
43
  // works. This keeps the function deterministic and unit-testable in Node.
44
- const hasStorage = opts != null && Object.prototype.hasOwnProperty.call(opts, 'storage');
44
+ const hasStorage = opts !== undefined && opts !== null && Object.hasOwn(opts, 'storage');
45
45
  const storage = hasStorage ? opts.storage : safeLocalStorage();
46
46
  if (storage) {
47
47
  let stored = null;
@@ -52,7 +52,7 @@ export function resolveLocale(opts) {
52
52
  if (stored && isSupported(stored))
53
53
  return stored;
54
54
  }
55
- const hasNav = opts != null && Object.prototype.hasOwnProperty.call(opts, 'navLang');
55
+ const hasNav = opts !== undefined && opts !== null && Object.hasOwn(opts, 'navLang');
56
56
  const navLang = hasNav ? opts.navLang : safeNavLang();
57
57
  if (navLang) {
58
58
  const prefix = navLang.toLowerCase().split('-')[0];
@@ -112,6 +112,14 @@ let initialized = false;
112
112
  * fetch can never overwrite a newer selection.
113
113
  */
114
114
  let localeReqId = 0;
115
+ /** Commit the English fallback catalog. Isolated so an awaited fetch cannot race the write. */
116
+ function rememberEnglishCatalog(catalog) {
117
+ enCatalog = catalog;
118
+ }
119
+ /** Mark i18n ready. Isolated so the flag write is not paired with a stale read across await. */
120
+ function markI18nInitialized() {
121
+ initialized = true;
122
+ }
115
123
  /** Fetch a locale catalog JSON. Best-effort: returns {} on any failure. */
116
124
  async function fetchCatalog(locale) {
117
125
  try {
@@ -141,7 +149,7 @@ export async function initI18n() {
141
149
  currentLocale = resolveLocale();
142
150
  }
143
151
  if (!Object.keys(enCatalog).length) {
144
- enCatalog = await fetchCatalog('en');
152
+ rememberEnglishCatalog(await fetchCatalog('en'));
145
153
  }
146
154
  if (currentLocale === 'en') {
147
155
  activeCatalog = enCatalog;
@@ -149,7 +157,7 @@ export async function initI18n() {
149
157
  else {
150
158
  activeCatalog = await fetchCatalog(currentLocale);
151
159
  }
152
- initialized = true;
160
+ markI18nInitialized();
153
161
  syncHtmlLang(currentLocale);
154
162
  applyTranslations();
155
163
  }
@@ -187,7 +195,7 @@ export async function setLocale(locale) {
187
195
  // A newer setLocale superseded this one: do not assign enCatalog.
188
196
  if (myReqId !== localeReqId)
189
197
  return;
190
- enCatalog = en;
198
+ rememberEnglishCatalog(en);
191
199
  }
192
200
  const fetched = next === 'en' ? enCatalog : await fetchCatalog(next);
193
201
  // Stale request: discard the fetched catalog so it can never overwrite a
@@ -195,7 +203,7 @@ export async function setLocale(locale) {
195
203
  if (myReqId !== localeReqId)
196
204
  return;
197
205
  activeCatalog = fetched;
198
- initialized = true;
206
+ markI18nInitialized();
199
207
  syncHtmlLang(next);
200
208
  applyTranslations();
201
209
  }
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","sourceRoot":"","sources":["i18n.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","sourceRoot":"","sources":["i18n.ts"],"names":[],"mappings":"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"}
@@ -51,14 +51,14 @@ export function resolveLocale(opts?: {
51
51
  // wants that value: `null` skips the step. A property that is absent falls
52
52
  // back to the real browser global, so the no-argument browser path still
53
53
  // works. This keeps the function deterministic and unit-testable in Node.
54
- const hasStorage = opts != null && Object.prototype.hasOwnProperty.call(opts, 'storage');
54
+ const hasStorage = opts !== undefined && opts !== null && Object.hasOwn(opts, 'storage');
55
55
  const storage = hasStorage ? opts!.storage : safeLocalStorage();
56
56
  if (storage) {
57
57
  let stored: string | null = null;
58
58
  try { stored = storage.getItem(LOCALE_STORAGE_KEY); } catch { /* privacy mode */ }
59
59
  if (stored && isSupported(stored)) return stored;
60
60
  }
61
- const hasNav = opts != null && Object.prototype.hasOwnProperty.call(opts, 'navLang');
61
+ const hasNav = opts !== undefined && opts !== null && Object.hasOwn(opts, 'navLang');
62
62
  const navLang = hasNav ? opts!.navLang : safeNavLang();
63
63
  if (navLang) {
64
64
  const prefix = navLang.toLowerCase().split('-')[0];
@@ -128,6 +128,16 @@ let initialized = false;
128
128
  */
129
129
  let localeReqId = 0;
130
130
 
131
+ /** Commit the English fallback catalog. Isolated so an awaited fetch cannot race the write. */
132
+ function rememberEnglishCatalog(catalog: Record<string, string>): void {
133
+ enCatalog = catalog;
134
+ }
135
+
136
+ /** Mark i18n ready. Isolated so the flag write is not paired with a stale read across await. */
137
+ function markI18nInitialized(): void {
138
+ initialized = true;
139
+ }
140
+
131
141
  /** Fetch a locale catalog JSON. Best-effort: returns {} on any failure. */
132
142
  async function fetchCatalog(locale: string): Promise<Record<string, string>> {
133
143
  try {
@@ -156,14 +166,14 @@ export async function initI18n(): Promise<void> {
156
166
  currentLocale = resolveLocale();
157
167
  }
158
168
  if (!Object.keys(enCatalog).length) {
159
- enCatalog = await fetchCatalog('en');
169
+ rememberEnglishCatalog(await fetchCatalog('en'));
160
170
  }
161
171
  if (currentLocale === 'en') {
162
172
  activeCatalog = enCatalog;
163
173
  } else {
164
174
  activeCatalog = await fetchCatalog(currentLocale);
165
175
  }
166
- initialized = true;
176
+ markI18nInitialized();
167
177
  syncHtmlLang(currentLocale);
168
178
  applyTranslations();
169
179
  }
@@ -201,14 +211,14 @@ export async function setLocale(locale: string): Promise<void> {
201
211
  const en = await fetchCatalog('en');
202
212
  // A newer setLocale superseded this one: do not assign enCatalog.
203
213
  if (myReqId !== localeReqId) return;
204
- enCatalog = en;
214
+ rememberEnglishCatalog(en);
205
215
  }
206
216
  const fetched = next === 'en' ? enCatalog : await fetchCatalog(next);
207
217
  // Stale request: discard the fetched catalog so it can never overwrite a
208
218
  // newer selection (e.g. a slow German fetch finishing after en was chosen).
209
219
  if (myReqId !== localeReqId) return;
210
220
  activeCatalog = fetched;
211
- initialized = true;
221
+ markI18nInitialized();
212
222
  syncHtmlLang(next);
213
223
  applyTranslations();
214
224
  }
package/public/src/sw.ts CHANGED
@@ -34,6 +34,7 @@ const STATIC_ASSETS: readonly string[] = [
34
34
  '/src/api.js',
35
35
  '/src/api-types.js',
36
36
  '/src/app.js',
37
+ '/src/browser-window.js',
37
38
  '/src/components/modals.js',
38
39
  '/src/components/toast.js',
39
40
  '/src/constants.js',
@@ -73,6 +73,64 @@ export function setLoading(btn, yes, text) {
73
73
  btn.textContent = text;
74
74
  }
75
75
  }
76
+ /** Restores a button to its enabled state with the given label, updating the
77
+ * first inner span when present. No-op when `btn` is null. */
78
+ export function resetButton(btn, label) {
79
+ if (btn) {
80
+ btn.disabled = false;
81
+ const sp = btn.querySelector('span');
82
+ if (sp)
83
+ sp.textContent = label;
84
+ }
85
+ }
86
+ /** Sets a form element's value by id, skipping empty or undefined values.
87
+ * Looks up the element as input, select, or textarea. */
88
+ export function setFormValue(id, val) {
89
+ if (!val)
90
+ return;
91
+ const el = document.getElementById(id);
92
+ if (el)
93
+ el.value = val;
94
+ }
95
+ /** Renders a single issue or change row with an icon, message, and optional
96
+ * item link. The `contentStyle` parameter controls the inner div's style
97
+ * (pass an empty string for no style attribute). */
98
+ export function issueRow(icon, iconColor, message, itemId, contentStyle = 'flex:1') {
99
+ return `<div style="display:flex;gap:8px;align-items:flex-start;padding:8px 0;border-bottom:1px solid var(--border)">
100
+ <span style="color:${iconColor};flex-shrink:0">${icon}</span>
101
+ <div${contentStyle ? ` style="${contentStyle}"` : ''}>
102
+ <div style="font-size:13px">${escHtml(message)}</div>
103
+ ${itemId ? `<div style="font-size:11px;color:var(--text-muted);margin-top:2px"><a href="#" onclick="window.__app.openItemDetail('${escHtml(itemId)}');return false" style="color:var(--accent)">${escHtml(itemId)}</a></div>` : ''}
104
+ </div>
105
+ </div>`;
106
+ }
107
+ /** Renders the common name, slug, and description portion of a project card,
108
+ * leaving the wrapper and meta row for the caller. */
109
+ export function projectCardInfo(p) {
110
+ return `<div class="project-card-name">${escHtml(p.name)}</div>
111
+ <div class="project-card-slug mono">${escHtml(p.slug)}</div>
112
+ <div class="project-card-desc">${escHtml(p.description || 'No description')}</div>`;
113
+ }
114
+ /** Updates the header avatar initials and display name from the given user's
115
+ * profile data. */
116
+ export function updateHeaderUser(u) {
117
+ const initials = (u.display_name || u.email || '?').split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();
118
+ const avatarEl = document.getElementById('user-avatar');
119
+ if (avatarEl)
120
+ avatarEl.textContent = initials;
121
+ const nameEl = document.getElementById('user-name-display');
122
+ if (nameEl)
123
+ nameEl.textContent = u.display_name || u.email;
124
+ }
125
+ /** Shows an error message in the given inline error element and makes it
126
+ * visible, extracting the message from an unknown error value. No-op when
127
+ * `errEl` is null. */
128
+ export function showError(errEl, err) {
129
+ if (errEl) {
130
+ errEl.textContent = err instanceof Error ? err.message : String(err);
131
+ errEl.style.display = 'block';
132
+ }
133
+ }
76
134
  /** Build an HTML string of `n` placeholder skeleton rows for loading states. */
77
135
  export function skeletonRows(n = 5) {
78
136
  return Array.from({ length: n }, () => '<div class="skeleton skeleton-row"></div>').join('');
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,YAAY;AACZ,kEAAkE;AAClE,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE7D;;;;;;;;+CAQ+C;AAC/C,MAAM,UAAU,OAAO,CAAC,CAA4B;IAClD,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAClB,OAAO,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAC,QAAQ,CAAC,CAAC;AAC1G,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,MAAM,MAAM,GAA2B,EAAE,WAAW,EAAE,aAAa,EAAE,CAAC;IACtE,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;IACtC,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,IAAI,SAAS,CAAC;IAC5G,OAAO,oCAAoC,WAAW,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC;AACxG,CAAC;AAED;uDACuD;AACvD,MAAM,UAAU,WAAW,CAAC,CAAS;IACnC,OAAO,sCAAsC,CAAC,aAAa,CAAC,KAAK,eAAe,CAAC,CAAC,CAAC,IAAE,EAAE,WAAW,CAAC;AACrG,CAAC;AAED;kEACkE;AAClE,MAAM,UAAU,QAAQ,CAAC,CAAS;IAChC,OAAO,uCAAuC,OAAO,CAAC,CAAC,CAAC,KAAK,UAAU,CAAC,CAAC,CAAC,IAAE,GAAG,SAAS,CAAC;AAC3F,CAAC;AAED;;gDAEgD;AAChD,MAAM,UAAU,OAAO,CAAC,EAA6B;IACnD,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACnB,MAAM,CAAC,GAAG,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IACtC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,GAAC,IAAI,CAAC,CAAC;IAChC,IAAI,CAAC,GAAG,EAAE;QAAE,OAAO,UAAU,CAAC;IAC9B,IAAI,CAAC,GAAG,IAAI;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAC,EAAE,CAAC,OAAO,CAAC;IAChD,IAAI,CAAC,GAAG,KAAK;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAC,IAAI,CAAC,OAAO,CAAC;IACnD,IAAI,CAAC,GAAG,MAAM;QAAE,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAC,KAAK,CAAC,OAAO,CAAC;IACrD,OAAO,CAAC,CAAC,kBAAkB,EAAE,CAAC;AAChC,CAAC;AAED;oCACoC;AACpC,MAAM,UAAU,OAAO,CAAC,EAA6B;IACnD,IAAI,CAAC,EAAE;QAAE,OAAO,EAAE,CAAC;IACnB,OAAO,IAAI,IAAI,CAAC,EAAE,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE,EAAE,IAAI,EAAC,SAAS,EAAE,KAAK,EAAC,OAAO,EAAE,GAAG,EAAC,SAAS,EAAE,CAAC,CAAC;AACpG,CAAC;AAED;;yBAEyB;AACzB,MAAM,UAAU,UAAU,CAAC,GAAsB,EAAE,GAAY,EAAE,IAAa;IAC5E,GAAG,CAAC,QAAQ,GAAG,GAAG,CAAC;IACnB,IAAI,IAAI,EAAE,CAAC;QACT,MAAM,IAAI,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,IAAI;YAAE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;;YAC7B,GAAG,CAAC,WAAW,GAAG,IAAI,CAAC;IAC9B,CAAC;AACH,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,YAAY,CAAC,CAAC,GAAG,CAAC;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,EAAE,GAAG,EAAE,CAAC,2CAA2C,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC7F,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,aAAa,CAAC,CAAC,GAAG,CAAC;IACjC,OAAO,KAAK,CAAC,IAAI,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,EAAE,GAAG,EAAE,CAAC,4CAA4C,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AAC9F,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["utils.ts"],"names":[],"mappings":"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"}