@unbrained/pm-web 2026.8.7 → 2026.8.14
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.
- package/CHANGELOG.md +16 -0
- package/dist/app.d.ts +16 -0
- package/dist/app.js +16 -0
- package/dist/app.js.map +1 -1
- package/dist/auth.d.ts +5 -0
- package/dist/auth.js.map +1 -1
- package/dist/board.d.ts +53 -0
- package/dist/board.js +48 -0
- package/dist/board.js.map +1 -1
- package/dist/crypto.d.ts +23 -0
- package/dist/crypto.js +32 -0
- package/dist/crypto.js.map +1 -1
- package/dist/db.d.ts +19 -0
- package/dist/db.js +19 -0
- package/dist/db.js.map +1 -1
- package/dist/ical.d.ts +72 -0
- package/dist/ical.js +73 -0
- package/dist/ical.js.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.js +52 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/auth.d.ts +8 -0
- package/dist/middleware/auth.js.map +1 -1
- package/dist/oidc.d.ts +87 -0
- package/dist/oidc.js +113 -0
- package/dist/oidc.js.map +1 -1
- package/dist/routes/admin.js +15 -0
- package/dist/routes/admin.js.map +1 -1
- package/dist/routes/github.js +13 -0
- package/dist/routes/github.js.map +1 -1
- package/dist/routes/oidc.d.ts +10 -0
- package/dist/routes/oidc.js +51 -0
- package/dist/routes/oidc.js.map +1 -1
- package/dist/routes/pm.js +132 -1
- package/dist/routes/pm.js.map +1 -1
- package/dist/routes/sharing.js +8 -0
- package/dist/routes/sharing.js.map +1 -1
- package/dist/services/mutation-event-watcher.js +13 -0
- package/dist/services/mutation-event-watcher.js.map +1 -1
- package/dist/services/pm-runner.d.ts +82 -0
- package/dist/services/pm-runner.js +121 -0
- package/dist/services/pm-runner.js.map +1 -1
- package/dist/services/project-watcher.d.ts +22 -0
- package/dist/services/project-watcher.js +51 -0
- package/dist/services/project-watcher.js.map +1 -1
- package/dist/services/realtime-bus.d.ts +46 -0
- package/dist/services/realtime-bus.js +59 -0
- package/dist/services/realtime-bus.js.map +1 -1
- package/dist/services/sse.d.ts +134 -0
- package/dist/services/sse.js +145 -0
- package/dist/services/sse.js.map +1 -1
- package/manifest.json +1 -1
- package/package.json +5 -3
- package/public/src/app.js +44 -1
- package/public/src/app.js.map +1 -1
- package/public/src/app.ts +44 -1
- package/public/src/components/modals.js +11 -0
- package/public/src/components/modals.js.map +1 -1
- package/public/src/components/modals.ts +11 -0
- package/public/src/components/toast.js +2 -0
- package/public/src/components/toast.js.map +1 -1
- package/public/src/components/toast.ts +2 -0
- package/public/src/constants.js +21 -4
- package/public/src/constants.js.map +1 -1
- package/public/src/constants.ts +21 -4
- package/public/src/filters.js +10 -7
- package/public/src/filters.js.map +1 -1
- package/public/src/filters.ts +10 -7
- package/public/src/i18n.js +11 -1
- package/public/src/i18n.js.map +1 -1
- package/public/src/i18n.ts +13 -1
- package/public/src/state.js +3 -0
- package/public/src/state.js.map +1 -1
- package/public/src/state.ts +3 -0
- package/public/src/sw.ts +71 -7
- package/public/src/theme.js +13 -1
- package/public/src/theme.js.map +1 -1
- package/public/src/theme.ts +15 -1
- package/public/src/types.ts +2 -0
- package/public/src/utils.js +26 -1
- package/public/src/utils.js.map +1 -1
- package/public/src/utils.ts +26 -1
- package/public/src/views/activity.js +1 -0
- package/public/src/views/activity.js.map +1 -1
- package/public/src/views/activity.ts +1 -0
- package/public/src/views/admin.js +134 -0
- package/public/src/views/admin.js.map +1 -1
- package/public/src/views/admin.ts +134 -0
- package/public/src/views/auth.js +31 -0
- package/public/src/views/auth.js.map +1 -1
- package/public/src/views/auth.ts +31 -0
- package/public/src/views/calendar.js +3 -0
- package/public/src/views/calendar.js.map +1 -1
- package/public/src/views/calendar.ts +3 -0
- package/public/src/views/comments-audit.js +1 -0
- package/public/src/views/comments-audit.js.map +1 -1
- package/public/src/views/comments-audit.ts +1 -0
- package/public/src/views/config.js +67 -0
- package/public/src/views/config.js.map +1 -1
- package/public/src/views/config.ts +67 -0
- package/public/src/views/context.js +2 -0
- package/public/src/views/context.js.map +1 -1
- package/public/src/views/context.ts +2 -0
- package/public/src/views/create.js +3 -0
- package/public/src/views/create.js.map +1 -1
- package/public/src/views/create.ts +3 -0
- package/public/src/views/dedupe.js +1 -0
- package/public/src/views/dedupe.js.map +1 -1
- package/public/src/views/dedupe.ts +1 -0
- package/public/src/views/export.js +6 -0
- package/public/src/views/export.js.map +1 -1
- package/public/src/views/export.ts +6 -0
- package/public/src/views/github.js +58 -0
- package/public/src/views/github.js.map +1 -1
- package/public/src/views/github.ts +58 -0
- package/public/src/views/graph-canvas.js +18 -0
- package/public/src/views/graph-canvas.js.map +1 -1
- package/public/src/views/graph-canvas.ts +20 -0
- package/public/src/views/graph.js +41 -0
- package/public/src/views/graph.js.map +1 -1
- package/public/src/views/graph.ts +42 -1
- package/public/src/views/groups.js +45 -0
- package/public/src/views/groups.js.map +1 -1
- package/public/src/views/groups.ts +45 -0
- package/public/src/views/guide.js +3 -0
- package/public/src/views/guide.js.map +1 -1
- package/public/src/views/guide.ts +3 -0
- package/public/src/views/health.js +3 -0
- package/public/src/views/health.js.map +1 -1
- package/public/src/views/health.ts +3 -0
- package/public/src/views/items.js +196 -6
- package/public/src/views/items.js.map +1 -1
- package/public/src/views/items.ts +196 -6
- package/public/src/views/normalize.js +2 -0
- package/public/src/views/normalize.js.map +1 -1
- package/public/src/views/normalize.ts +2 -0
- package/public/src/views/packages.js +41 -0
- package/public/src/views/packages.js.map +1 -1
- package/public/src/views/packages.ts +41 -0
- package/public/src/views/plan-execution.js +6 -0
- package/public/src/views/plan-execution.js.map +1 -1
- package/public/src/views/plan-execution.ts +8 -0
- package/public/src/views/plan.js +145 -0
- package/public/src/views/plan.js.map +1 -1
- package/public/src/views/plan.ts +146 -1
- package/public/src/views/projects.js +60 -0
- package/public/src/views/projects.js.map +1 -1
- package/public/src/views/projects.ts +60 -0
- package/public/src/views/router.js +43 -0
- package/public/src/views/router.js.map +1 -1
- package/public/src/views/router.ts +43 -0
- package/public/src/views/search.js +5 -0
- package/public/src/views/search.js.map +1 -1
- package/public/src/views/search.ts +5 -0
- package/public/src/views/settings.js +36 -0
- package/public/src/views/settings.js.map +1 -1
- package/public/src/views/settings.ts +36 -0
- package/public/src/views/shared.js +1 -0
- package/public/src/views/shared.js.map +1 -1
- package/public/src/views/shared.ts +1 -0
- package/public/src/views/sharing.js +32 -0
- package/public/src/views/sharing.js.map +1 -1
- package/public/src/views/sharing.ts +32 -0
- package/public/src/views/stats.js +1 -0
- package/public/src/views/stats.js.map +1 -1
- package/public/src/views/stats.ts +1 -0
- package/public/src/views/templates.js +4 -0
- package/public/src/views/templates.js.map +1 -1
- package/public/src/views/templates.ts +4 -0
- package/public/src/views/validate.js +1 -0
- package/public/src/views/validate.js.map +1 -1
- package/public/src/views/validate.ts +1 -0
- package/public/sw.js +60 -5
|
@@ -3,22 +3,29 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════
|
|
4
4
|
import { escHtml } from '../utils.js';
|
|
5
5
|
|
|
6
|
+
/** Display the modal with the given id by setting its display style to flex. */
|
|
6
7
|
export function showModal(id: string): void {
|
|
7
8
|
const modal = document.getElementById(id);
|
|
8
9
|
if (modal) modal.style.display = 'flex';
|
|
9
10
|
}
|
|
10
11
|
|
|
12
|
+
/** Hide the modal with the given id by setting its display style to none. */
|
|
11
13
|
export function hideModal(id: string): void {
|
|
12
14
|
const modal = document.getElementById(id);
|
|
13
15
|
if (modal) modal.style.display = 'none';
|
|
14
16
|
}
|
|
15
17
|
|
|
18
|
+
/** Hide every modal backdrop element in the document by setting each to
|
|
19
|
+
* display none. */
|
|
16
20
|
export function closeAllModals(): void {
|
|
17
21
|
document.querySelectorAll('.modal-backdrop').forEach(m => {
|
|
18
22
|
(m as HTMLElement).style.display = 'none';
|
|
19
23
|
});
|
|
20
24
|
}
|
|
21
25
|
|
|
26
|
+
/** Build (or rebuild) a modal element with the given id, title, body, and
|
|
27
|
+
* optional footer markup, append it under `#modal-container`, and wire its
|
|
28
|
+
* backdrop-click-to-close behavior. Returns the created modal element. */
|
|
22
29
|
export function createModal(
|
|
23
30
|
id: string,
|
|
24
31
|
title: string,
|
|
@@ -47,6 +54,10 @@ export function createModal(
|
|
|
47
54
|
return el;
|
|
48
55
|
}
|
|
49
56
|
|
|
57
|
+
/** Open a confirmation modal with a title and description; when the user
|
|
58
|
+
* clicks the confirm button the modal closes and `onConfirm` runs. The
|
|
59
|
+
* `danger` flag switches the styling and default labels to a destructive
|
|
60
|
+
* action, and `labels` overrides the button text. */
|
|
50
61
|
export function confirmDialog(
|
|
51
62
|
title: string,
|
|
52
63
|
desc: string,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","sourceRoot":"","sources":["toast.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,KAAK,CAAC,GAAW,EAAE,IAAI,GAAc,MAAM;IACzD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,EAAE,CAAC,SAAS,GAAG,eAAe,IAAI,EAAE,CAAC;IACrC,EAAE,CAAC,WAAW,GAAG,GAAG,CAAC;IACrB,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC7D,IAAI,SAAS;QAAE,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACzC,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC"}
|
|
1
|
+
{"version":3,"file":"toast.js","sourceRoot":"","sources":["toast.ts"],"names":[],"mappings":"AAKA;wDACwD;AACxD,MAAM,UAAU,KAAK,CAAC,GAAW,EAAE,IAAI,GAAc,MAAM;IACzD,MAAM,EAAE,GAAG,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IACzC,EAAE,CAAC,SAAS,GAAG,eAAe,IAAI,EAAE,CAAC;IACrC,EAAE,CAAC,WAAW,GAAG,GAAG,CAAC;IACrB,MAAM,SAAS,GAAG,QAAQ,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;IAC7D,IAAI,SAAS;QAAE,SAAS,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IACzC,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════
|
|
4
4
|
import type { ToastType } from '../types.js';
|
|
5
5
|
|
|
6
|
+
/** Show a transient toast notification of the given type, appending it to the
|
|
7
|
+
* toast container and removing it after 3.5 seconds. */
|
|
6
8
|
export function toast(msg: string, type: ToastType = 'info'): void {
|
|
7
9
|
const el = document.createElement('div');
|
|
8
10
|
el.className = `toast toast-${type}`;
|
package/public/src/constants.js
CHANGED
|
@@ -1,26 +1,43 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/** Item-type names kept in sync with the pm CLI builtins, used as the
|
|
2
|
+
* offline fallback when the live project schema is unavailable. Prefer
|
|
3
|
+
* `getTypes()` everywhere so the fetched schema wins when present. */
|
|
4
4
|
export const FALLBACK_TYPES = [
|
|
5
5
|
'Task', 'Feature', 'Issue', 'Epic', 'Milestone', 'Decision', 'Chore', 'Event', 'Meeting', 'Reminder', 'Plan'
|
|
6
6
|
];
|
|
7
|
+
/** Lifecycle status names kept in sync with the pm CLI builtins, used as the
|
|
8
|
+
* offline fallback when the live project schema is unavailable. Prefer
|
|
9
|
+
* `getStatuses()` everywhere so the fetched schema wins when present. */
|
|
7
10
|
export const FALLBACK_STATUSES = ['draft', 'open', 'in_progress', 'blocked', 'closed', 'canceled'];
|
|
8
|
-
|
|
11
|
+
/** Back-compat alias for `FALLBACK_TYPES`, retained to avoid a widespread
|
|
12
|
+
* refactor; new code should call `getTypes()` instead. */
|
|
9
13
|
export const TYPES = FALLBACK_TYPES;
|
|
14
|
+
/** Back-compat alias for `FALLBACK_STATUSES`, retained to avoid a widespread
|
|
15
|
+
* refactor; new code should call `getStatuses()` instead. */
|
|
10
16
|
export const STATUSES = FALLBACK_STATUSES;
|
|
17
|
+
/** Return the item types for a project, preferring the live `schema.types`
|
|
18
|
+
* when non-empty and falling back to the built-in `FALLBACK_TYPES`. */
|
|
11
19
|
export function getTypes(schema) {
|
|
12
20
|
return schema?.types?.length ? schema.types : [...FALLBACK_TYPES];
|
|
13
21
|
}
|
|
22
|
+
/** Return the lifecycle statuses for a project, preferring the live
|
|
23
|
+
* `schema.statuses` when non-empty and falling back to the built-in
|
|
24
|
+
* `FALLBACK_STATUSES`. */
|
|
14
25
|
export function getStatuses(schema) {
|
|
15
26
|
return schema?.statuses?.length ? schema.statuses : [...FALLBACK_STATUSES];
|
|
16
27
|
}
|
|
28
|
+
/** Map of item-type name to the single-character glyph shown in the UI for
|
|
29
|
+
* that type. */
|
|
17
30
|
export const TYPE_ICONS = {
|
|
18
31
|
Task: '✓', Feature: '★', Issue: '⚠', Epic: '◈',
|
|
19
32
|
Milestone: '⚑', Decision: '⚖', Chore: '⚙', Event: '◷', Meeting: '◉', Reminder: '◉', Plan: '◧'
|
|
20
33
|
};
|
|
34
|
+
/** Map of numeric priority (0–4) to its human-readable label, used for
|
|
35
|
+
* tooltips and accessibility text on priority indicators. */
|
|
21
36
|
export const PRIORITY_LABELS = {
|
|
22
37
|
0: 'Critical', 1: 'High', 2: 'Medium', 3: 'Low', 4: 'Minimal'
|
|
23
38
|
};
|
|
39
|
+
/** Fixed list of navigation view identifiers in display order; mirrors the
|
|
40
|
+
* routing keys accepted by the SPA router. */
|
|
24
41
|
export const VIEW_NAMES = [
|
|
25
42
|
'projects', 'items', 'create', 'activity', 'search', 'stats', 'calendar',
|
|
26
43
|
'context', 'graph', 'sharing', 'groups', 'health', 'dedupe', 'validate', 'settings',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","sourceRoot":"","sources":["constants.ts"],"names":[],"mappings":"AAKA
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["constants.ts"],"names":[],"mappings":"AAKA;;sEAEsE;AACtE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,MAAM,EAAC,SAAS,EAAC,OAAO,EAAC,MAAM,EAAC,WAAW,EAAC,UAAU,EAAC,OAAO,EAAC,OAAO,EAAC,SAAS,EAAC,UAAU,EAAC,MAAM;CAC1F,CAAC;AAEX;;yEAEyE;AACzE,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,OAAO,EAAC,MAAM,EAAC,aAAa,EAAC,SAAS,EAAC,QAAQ,EAAC,UAAU,CAAU,CAAC;AAEvG;0DAC0D;AAC1D,MAAM,CAAC,MAAM,KAAK,GAAG,cAAc,CAAC;AACpC;6DAC6D;AAC7D,MAAM,CAAC,MAAM,QAAQ,GAAG,iBAAiB,CAAC;AAE1C;uEACuE;AACvE,MAAM,UAAU,QAAQ,CAAC,MAA6B;IACpD,OAAO,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,cAAc,CAAC,CAAC;AACpE,CAAC;AAED;;0BAE0B;AAC1B,MAAM,UAAU,WAAW,CAAC,MAA6B;IACvD,OAAO,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,iBAAiB,CAAC,CAAC;AAC7E,CAAC;AAED;gBACgB;AAChB,MAAM,CAAC,MAAM,UAAU,GAA2B;IAChD,IAAI,EAAC,GAAG,EAAE,OAAO,EAAC,GAAG,EAAE,KAAK,EAAC,GAAG,EAAE,IAAI,EAAC,GAAG;IAC1C,SAAS,EAAC,GAAG,EAAE,QAAQ,EAAC,GAAG,EAAE,KAAK,EAAC,GAAG,EAAE,KAAK,EAAC,GAAG,EAAE,OAAO,EAAC,GAAG,EAAE,QAAQ,EAAC,GAAG,EAAE,IAAI,EAAC,GAAG;CACvF,CAAC;AAEF;6DAC6D;AAC7D,MAAM,CAAC,MAAM,eAAe,GAA2B;IACrD,CAAC,EAAC,UAAU,EAAE,CAAC,EAAC,MAAM,EAAE,CAAC,EAAC,QAAQ,EAAE,CAAC,EAAC,KAAK,EAAE,CAAC,EAAC,SAAS;CACzD,CAAC;AAEF;8CAC8C;AAC9C,MAAM,CAAC,MAAM,UAAU,GAAG;IACxB,UAAU,EAAC,OAAO,EAAC,QAAQ,EAAC,UAAU,EAAC,QAAQ,EAAC,OAAO,EAAC,UAAU;IAClE,SAAS,EAAC,OAAO,EAAC,SAAS,EAAC,QAAQ,EAAC,QAAQ,EAAC,QAAQ,EAAC,UAAU,EAAC,UAAU;IAC5E,QAAQ,EAAC,QAAQ,EAAC,WAAW,EAAC,QAAQ,EAAC,WAAW,EAAC,UAAU,EAAC,gBAAgB,EAAC,QAAQ,EAAC,OAAO;IAC/F,OAAO,EAAC,MAAM;CACN,CAAC"}
|
package/public/src/constants.ts
CHANGED
|
@@ -3,36 +3,53 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════
|
|
4
4
|
import type { ProjectSchema } from './types.js';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
/** Item-type names kept in sync with the pm CLI builtins, used as the
|
|
7
|
+
* offline fallback when the live project schema is unavailable. Prefer
|
|
8
|
+
* `getTypes()` everywhere so the fetched schema wins when present. */
|
|
9
9
|
export const FALLBACK_TYPES = [
|
|
10
10
|
'Task','Feature','Issue','Epic','Milestone','Decision','Chore','Event','Meeting','Reminder','Plan'
|
|
11
11
|
] as const;
|
|
12
12
|
|
|
13
|
+
/** Lifecycle status names kept in sync with the pm CLI builtins, used as the
|
|
14
|
+
* offline fallback when the live project schema is unavailable. Prefer
|
|
15
|
+
* `getStatuses()` everywhere so the fetched schema wins when present. */
|
|
13
16
|
export const FALLBACK_STATUSES = ['draft','open','in_progress','blocked','closed','canceled'] as const;
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
/** Back-compat alias for `FALLBACK_TYPES`, retained to avoid a widespread
|
|
19
|
+
* refactor; new code should call `getTypes()` instead. */
|
|
16
20
|
export const TYPES = FALLBACK_TYPES;
|
|
21
|
+
/** Back-compat alias for `FALLBACK_STATUSES`, retained to avoid a widespread
|
|
22
|
+
* refactor; new code should call `getStatuses()` instead. */
|
|
17
23
|
export const STATUSES = FALLBACK_STATUSES;
|
|
18
24
|
|
|
25
|
+
/** Return the item types for a project, preferring the live `schema.types`
|
|
26
|
+
* when non-empty and falling back to the built-in `FALLBACK_TYPES`. */
|
|
19
27
|
export function getTypes(schema?: ProjectSchema | null): string[] {
|
|
20
28
|
return schema?.types?.length ? schema.types : [...FALLBACK_TYPES];
|
|
21
29
|
}
|
|
22
30
|
|
|
31
|
+
/** Return the lifecycle statuses for a project, preferring the live
|
|
32
|
+
* `schema.statuses` when non-empty and falling back to the built-in
|
|
33
|
+
* `FALLBACK_STATUSES`. */
|
|
23
34
|
export function getStatuses(schema?: ProjectSchema | null): string[] {
|
|
24
35
|
return schema?.statuses?.length ? schema.statuses : [...FALLBACK_STATUSES];
|
|
25
36
|
}
|
|
26
37
|
|
|
38
|
+
/** Map of item-type name to the single-character glyph shown in the UI for
|
|
39
|
+
* that type. */
|
|
27
40
|
export const TYPE_ICONS: Record<string, string> = {
|
|
28
41
|
Task:'✓', Feature:'★', Issue:'⚠', Epic:'◈',
|
|
29
42
|
Milestone:'⚑', Decision:'⚖', Chore:'⚙', Event:'◷', Meeting:'◉', Reminder:'◉', Plan:'◧'
|
|
30
43
|
};
|
|
31
44
|
|
|
45
|
+
/** Map of numeric priority (0–4) to its human-readable label, used for
|
|
46
|
+
* tooltips and accessibility text on priority indicators. */
|
|
32
47
|
export const PRIORITY_LABELS: Record<number, string> = {
|
|
33
48
|
0:'Critical', 1:'High', 2:'Medium', 3:'Low', 4:'Minimal'
|
|
34
49
|
};
|
|
35
50
|
|
|
51
|
+
/** Fixed list of navigation view identifiers in display order; mirrors the
|
|
52
|
+
* routing keys accepted by the SPA router. */
|
|
36
53
|
export const VIEW_NAMES = [
|
|
37
54
|
'projects','items','create','activity','search','stats','calendar',
|
|
38
55
|
'context','graph','sharing','groups','health','dedupe','validate','settings',
|
package/public/src/filters.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
/** Item filter state with every dimension empty, used as the base value when
|
|
2
|
+
* resetting or parsing filters. */
|
|
1
3
|
export const EMPTY_FILTERS = {
|
|
2
4
|
status: '', type: '', priority: '', sprint: '', release: '', assignee: '', tag: '',
|
|
3
5
|
};
|
|
@@ -5,8 +7,8 @@ export const EMPTY_FILTERS = {
|
|
|
5
7
|
const FILTER_KEYS = [
|
|
6
8
|
'status', 'type', 'priority', 'sprint', 'release', 'assignee', 'tag',
|
|
7
9
|
];
|
|
8
|
-
|
|
9
|
-
|
|
10
|
+
/** Serialize the active (non-empty) filter dimensions into a `URLSearchParams`
|
|
11
|
+
* in a stable key order, omitting empty values so generated URLs stay clean. */
|
|
10
12
|
export function filtersToSearchParams(f) {
|
|
11
13
|
const params = new URLSearchParams();
|
|
12
14
|
for (const key of FILTER_KEYS) {
|
|
@@ -16,13 +18,13 @@ export function filtersToSearchParams(f) {
|
|
|
16
18
|
}
|
|
17
19
|
return params;
|
|
18
20
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
+
/** Serialize filters to a URL query string without the leading `?`, returning
|
|
22
|
+
* an empty string when no dimensions are active. */
|
|
21
23
|
export function filtersToQueryString(f) {
|
|
22
24
|
return filtersToSearchParams(f).toString();
|
|
23
25
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
+
/** Parse filter dimensions back out of a `URLSearchParams` or query string,
|
|
27
|
+
* defaulting missing known keys to empty strings and ignoring unknown keys. */
|
|
26
28
|
export function filtersFromSearchParams(input) {
|
|
27
29
|
const params = typeof input === 'string' ? new URLSearchParams(input) : input;
|
|
28
30
|
const result = { ...EMPTY_FILTERS };
|
|
@@ -33,7 +35,8 @@ export function filtersFromSearchParams(input) {
|
|
|
33
35
|
}
|
|
34
36
|
return result;
|
|
35
37
|
}
|
|
36
|
-
|
|
38
|
+
/** Return true when at least one filter dimension has a non-empty trimmed
|
|
39
|
+
* value. */
|
|
37
40
|
export function hasActiveFilters(f) {
|
|
38
41
|
return FILTER_KEYS.some((key) => (f[key] ?? '').trim() !== '');
|
|
39
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"filters.js","sourceRoot":"","sources":["filters.ts"],"names":[],"mappings":"AAUA,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;CACnF,CAAC;AAEF,uDAAuD;AACvD,MAAM,WAAW,GAA0B;IACzC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK;CACrE,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"filters.js","sourceRoot":"","sources":["filters.ts"],"names":[],"mappings":"AAUA;mCACmC;AACnC,MAAM,CAAC,MAAM,aAAa,GAAgB;IACxC,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE;CACnF,CAAC;AAEF,uDAAuD;AACvD,MAAM,WAAW,GAA0B;IACzC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,EAAE,KAAK;CACrE,CAAC;AAEF;gFACgF;AAChF,MAAM,UAAU,qBAAqB,CAAC,CAAc;IAClD,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QACpC,IAAI,KAAK;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;oDACoD;AACpD,MAAM,UAAU,oBAAoB,CAAC,CAAc;IACjD,OAAO,qBAAqB,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;AAC7C,CAAC;AAED;+EAC+E;AAC/E,MAAM,UAAU,uBAAuB,CAAC,KAA+B;IACrE,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;IAC9E,MAAM,MAAM,GAAgB,EAAE,GAAG,aAAa,EAAE,CAAC;IACjD,KAAK,MAAM,GAAG,IAAI,WAAW,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9B,IAAI,KAAK,KAAK,IAAI;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IAC1C,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;YACY;AACZ,MAAM,UAAU,gBAAgB,CAAC,CAAc;IAC7C,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACjE,CAAC"}
|
package/public/src/filters.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
// free so they can be unit-tested without a DOM.
|
|
9
9
|
import type { ItemFilters } from './types.js';
|
|
10
10
|
|
|
11
|
+
/** Item filter state with every dimension empty, used as the base value when
|
|
12
|
+
* resetting or parsing filters. */
|
|
11
13
|
export const EMPTY_FILTERS: ItemFilters = {
|
|
12
14
|
status: '', type: '', priority: '', sprint: '', release: '', assignee: '', tag: '',
|
|
13
15
|
};
|
|
@@ -17,8 +19,8 @@ const FILTER_KEYS: (keyof ItemFilters)[] = [
|
|
|
17
19
|
'status', 'type', 'priority', 'sprint', 'release', 'assignee', 'tag',
|
|
18
20
|
];
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
/** Serialize the active (non-empty) filter dimensions into a `URLSearchParams`
|
|
23
|
+
* in a stable key order, omitting empty values so generated URLs stay clean. */
|
|
22
24
|
export function filtersToSearchParams(f: ItemFilters): URLSearchParams {
|
|
23
25
|
const params = new URLSearchParams();
|
|
24
26
|
for (const key of FILTER_KEYS) {
|
|
@@ -28,14 +30,14 @@ export function filtersToSearchParams(f: ItemFilters): URLSearchParams {
|
|
|
28
30
|
return params;
|
|
29
31
|
}
|
|
30
32
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
+
/** Serialize filters to a URL query string without the leading `?`, returning
|
|
34
|
+
* an empty string when no dimensions are active. */
|
|
33
35
|
export function filtersToQueryString(f: ItemFilters): string {
|
|
34
36
|
return filtersToSearchParams(f).toString();
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
+
/** Parse filter dimensions back out of a `URLSearchParams` or query string,
|
|
40
|
+
* defaulting missing known keys to empty strings and ignoring unknown keys. */
|
|
39
41
|
export function filtersFromSearchParams(input: URLSearchParams | string): ItemFilters {
|
|
40
42
|
const params = typeof input === 'string' ? new URLSearchParams(input) : input;
|
|
41
43
|
const result: ItemFilters = { ...EMPTY_FILTERS };
|
|
@@ -46,7 +48,8 @@ export function filtersFromSearchParams(input: URLSearchParams | string): ItemFi
|
|
|
46
48
|
return result;
|
|
47
49
|
}
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
/** Return true when at least one filter dimension has a non-empty trimmed
|
|
52
|
+
* value. */
|
|
50
53
|
export function hasActiveFilters(f: ItemFilters): boolean {
|
|
51
54
|
return FILTER_KEYS.some((key) => (f[key] ?? '').trim() !== '');
|
|
52
55
|
}
|
package/public/src/i18n.js
CHANGED
|
@@ -153,7 +153,8 @@ export async function initI18n() {
|
|
|
153
153
|
syncHtmlLang(currentLocale);
|
|
154
154
|
applyTranslations();
|
|
155
155
|
}
|
|
156
|
-
/**
|
|
156
|
+
/** Return the locale currently selected for the session, set by `initI18n` or
|
|
157
|
+
* `setLocale`. Defaults to the English fallback before initialization. */
|
|
157
158
|
export function getLocale() {
|
|
158
159
|
return currentLocale;
|
|
159
160
|
}
|
|
@@ -220,6 +221,10 @@ export function applyTranslations(root) {
|
|
|
220
221
|
bindAttr(scope, '[data-i18n-placeholder]', 'data-i18n-placeholder', (el, v) => { el.setAttribute('placeholder', v); });
|
|
221
222
|
bindAttr(scope, '[data-i18n-aria]', 'data-i18n-aria', (el, v) => { el.setAttribute('aria-label', v); });
|
|
222
223
|
}
|
|
224
|
+
/** For every element in `scope` matching `selector`, read its `attr` data
|
|
225
|
+
* key, resolve the translation (with optional `data-i18n-params`
|
|
226
|
+
* substitutions), and pass the result to `setter`. Used to bind each
|
|
227
|
+
* `data-i18n*` flavor. */
|
|
223
228
|
function bindAttr(scope, selector, attr, setter) {
|
|
224
229
|
scope.querySelectorAll(selector).forEach((el) => {
|
|
225
230
|
const key = el.getAttribute(attr);
|
|
@@ -230,6 +235,8 @@ function bindAttr(scope, selector, attr, setter) {
|
|
|
230
235
|
setter(el, t(key, params));
|
|
231
236
|
});
|
|
232
237
|
}
|
|
238
|
+
/** Parse a `data-i18n-params` JSON string into a parameter object, returning
|
|
239
|
+
* `undefined` when the JSON is missing, malformed, or not a plain object. */
|
|
233
240
|
function safeParseParams(json) {
|
|
234
241
|
try {
|
|
235
242
|
const parsed = JSON.parse(json);
|
|
@@ -257,6 +264,9 @@ export function translateError(message) {
|
|
|
257
264
|
// Lazy reverse map: English error text → catalog key. Built from enCatalog
|
|
258
265
|
// so it tracks the catalog without a hand-maintained second table.
|
|
259
266
|
let cachedErrorReverse = null;
|
|
267
|
+
/** Build and cache a reverse lookup from English error text to its catalog
|
|
268
|
+
* key (covering only `error.*` entries), so server error strings can be
|
|
269
|
+
* mapped back to translatable keys at the display layer. */
|
|
260
270
|
function errorReverseMap() {
|
|
261
271
|
if (cachedErrorReverse)
|
|
262
272
|
return cachedErrorReverse;
|
package/public/src/i18n.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["i18n.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,wDAAwD;AACxD,kEAAkE;AAClE,EAAE;AACF,yEAAyE;AACzE,6EAA6E;AAC7E,2EAA2E;AAC3E,yEAAyE;AACzE,2EAA2E;AAC3E,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,uEAAuE;AACvE,gEAAgE;AAChE,EAAE;AACF,+DAA+D;AAC/D,6EAA6E;AAC7E,oEAAoE;AACpE,sEAAsE;AACtE,wCAAwC;AAExC,4DAA4D;AAC5D,MAAM,CAAC,MAAM,kBAAkB,GAAG,UAAU,CAAC;AAE7C,gFAAgF;AAChF,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"i18n.js","sourceRoot":"","sources":["i18n.ts"],"names":[],"mappings":"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"}
|
package/public/src/i18n.ts
CHANGED
|
@@ -24,6 +24,8 @@ export const LOCALE_STORAGE_KEY = 'pmLocale';
|
|
|
24
24
|
|
|
25
25
|
/** Locales shipped by this package. The first entry is the default/fallback. */
|
|
26
26
|
export const SUPPORTED_LOCALES = ['en', 'de', 'es', 'zh'] as const;
|
|
27
|
+
/** String literal union of every locale code this package ships a catalog
|
|
28
|
+
* for, derived from `SUPPORTED_LOCALES`. */
|
|
27
29
|
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
|
|
28
30
|
|
|
29
31
|
const DEFAULT_LOCALE: SupportedLocale = 'en';
|
|
@@ -166,7 +168,8 @@ export async function initI18n(): Promise<void> {
|
|
|
166
168
|
applyTranslations();
|
|
167
169
|
}
|
|
168
170
|
|
|
169
|
-
/**
|
|
171
|
+
/** Return the locale currently selected for the session, set by `initI18n` or
|
|
172
|
+
* `setLocale`. Defaults to the English fallback before initialization. */
|
|
170
173
|
export function getLocale(): SupportedLocale {
|
|
171
174
|
return currentLocale;
|
|
172
175
|
}
|
|
@@ -232,6 +235,10 @@ export function applyTranslations(root?: ParentNode): void {
|
|
|
232
235
|
bindAttr(scope, '[data-i18n-aria]', 'data-i18n-aria', (el, v) => { el.setAttribute('aria-label', v); });
|
|
233
236
|
}
|
|
234
237
|
|
|
238
|
+
/** For every element in `scope` matching `selector`, read its `attr` data
|
|
239
|
+
* key, resolve the translation (with optional `data-i18n-params`
|
|
240
|
+
* substitutions), and pass the result to `setter`. Used to bind each
|
|
241
|
+
* `data-i18n*` flavor. */
|
|
235
242
|
function bindAttr(
|
|
236
243
|
scope: ParentNode,
|
|
237
244
|
selector: string,
|
|
@@ -247,6 +254,8 @@ function bindAttr(
|
|
|
247
254
|
});
|
|
248
255
|
}
|
|
249
256
|
|
|
257
|
+
/** Parse a `data-i18n-params` JSON string into a parameter object, returning
|
|
258
|
+
* `undefined` when the JSON is missing, malformed, or not a plain object. */
|
|
250
259
|
function safeParseParams(json: string): Record<string, string | number> | undefined {
|
|
251
260
|
try {
|
|
252
261
|
const parsed = JSON.parse(json) as unknown;
|
|
@@ -274,6 +283,9 @@ export function translateError(message: string): string {
|
|
|
274
283
|
// Lazy reverse map: English error text → catalog key. Built from enCatalog
|
|
275
284
|
// so it tracks the catalog without a hand-maintained second table.
|
|
276
285
|
let cachedErrorReverse: Map<string, string> | null = null;
|
|
286
|
+
/** Build and cache a reverse lookup from English error text to its catalog
|
|
287
|
+
* key (covering only `error.*` entries), so server error strings can be
|
|
288
|
+
* mapped back to translatable keys at the display layer. */
|
|
277
289
|
function errorReverseMap(): Map<string, string> {
|
|
278
290
|
if (cachedErrorReverse) return cachedErrorReverse;
|
|
279
291
|
const map = new Map<string, string>();
|
package/public/src/state.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/** Mutable singleton holding the SPA's global application state: the signed-in
|
|
2
|
+
* user, loaded projects, the active project and view, item list and filters,
|
|
3
|
+
* and search state. Views read and mutate this directly. */
|
|
1
4
|
export const state = {
|
|
2
5
|
user: null,
|
|
3
6
|
projects: [],
|
package/public/src/state.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state.js","sourceRoot":"","sources":["state.ts"],"names":[],"mappings":"AAKA,MAAM,CAAC,MAAM,KAAK,GAAa;IAC7B,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,EAAE;IACZ,cAAc,EAAE,IAAI;IACpB,WAAW,EAAE,UAAU;IACvB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,EAAE;IACT,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IACnG,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,EAAE;IACjB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,IAAI;CACb,CAAC"}
|
|
1
|
+
{"version":3,"file":"state.js","sourceRoot":"","sources":["state.ts"],"names":[],"mappings":"AAKA;;4DAE4D;AAC5D,MAAM,CAAC,MAAM,KAAK,GAAa;IAC7B,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,EAAE;IACZ,cAAc,EAAE,IAAI;IACpB,WAAW,EAAE,UAAU;IACvB,OAAO,EAAE,OAAO;IAChB,KAAK,EAAE,EAAE;IACT,WAAW,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE;IACnG,WAAW,EAAE,EAAE;IACf,aAAa,EAAE,EAAE;IACjB,UAAU,EAAE,QAAQ;IACpB,SAAS,EAAE,CAAC;IACZ,MAAM,EAAE,IAAI;CACb,CAAC"}
|
package/public/src/state.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
// ═══════════════════════════════════════════════════════════════
|
|
4
4
|
import type { AppState } from './types.js';
|
|
5
5
|
|
|
6
|
+
/** Mutable singleton holding the SPA's global application state: the signed-in
|
|
7
|
+
* user, loaded projects, the active project and view, item list and filters,
|
|
8
|
+
* and search state. Views read and mutate this directly. */
|
|
6
9
|
export const state: AppState = {
|
|
7
10
|
user: null,
|
|
8
11
|
projects: [],
|
package/public/src/sw.ts
CHANGED
|
@@ -127,6 +127,9 @@ interface SyncEvent extends ExtendableEvent {
|
|
|
127
127
|
readonly tag: string;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
/** Open (creating on first run) the IndexedDB database backing the offline
|
|
131
|
+
* mutation queue, ensuring the `mutations` object store and its timestamp
|
|
132
|
+
* index exist. Resolves with the ready database handle. */
|
|
130
133
|
function openMutationDB(): Promise<IDBDatabase> {
|
|
131
134
|
return new Promise((resolve, reject) => {
|
|
132
135
|
const request = indexedDB.open(MUTATION_DB, 1);
|
|
@@ -185,18 +188,32 @@ async function queueMutation(method: string, path: string, body: unknown): Promi
|
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
|
|
188
|
-
|
|
191
|
+
/** The outcome of reading the mutation queue: either the mutations were read
|
|
192
|
+
* successfully, or the read failed and the queue state is unknown. The
|
|
193
|
+
* discriminator lets callers distinguish an empty queue from an unreadable
|
|
194
|
+
* one instead of collapsing both into `[]`. */
|
|
195
|
+
type QueueReadResult =
|
|
196
|
+
| { ok: true; mutations: QueuedMutation[] }
|
|
197
|
+
| { ok: false; error: unknown };
|
|
198
|
+
|
|
199
|
+
/** Read every queued mutation out of IndexedDB in insertion order. Returns
|
|
200
|
+
* `{ ok: true, mutations }` on success (including an empty array when the
|
|
201
|
+
* queue is genuinely empty) or `{ ok: false, error }` on storage failure so
|
|
202
|
+
* callers can distinguish an empty queue from an unreadable one instead of
|
|
203
|
+
* treating both as drained. */
|
|
204
|
+
async function getQueuedMutations(): Promise<QueueReadResult> {
|
|
189
205
|
try {
|
|
190
206
|
const db = await openMutationDB();
|
|
191
207
|
const tx = db.transaction(MUTATION_STORE, 'readonly');
|
|
192
208
|
const store = tx.objectStore(MUTATION_STORE);
|
|
193
|
-
|
|
209
|
+
const mutations = await new Promise<QueuedMutation[]>((resolve, reject) => {
|
|
194
210
|
const request = store.getAll();
|
|
195
211
|
request.onsuccess = () => resolve(request.result as QueuedMutation[]);
|
|
196
212
|
request.onerror = () => reject(request.error);
|
|
197
213
|
});
|
|
198
|
-
|
|
199
|
-
|
|
214
|
+
return { ok: true, mutations };
|
|
215
|
+
} catch (error) {
|
|
216
|
+
return { ok: false, error };
|
|
200
217
|
}
|
|
201
218
|
}
|
|
202
219
|
|
|
@@ -218,10 +235,22 @@ async function clearMutation(id: number): Promise<boolean> {
|
|
|
218
235
|
}
|
|
219
236
|
}
|
|
220
237
|
|
|
238
|
+
/** Replay queued mutations to the API in order, removing each on success and
|
|
239
|
+
* stopping at the first failure so it can retry later, then post-message the
|
|
240
|
+
* connected clients with how many were replayed or remain. A storage read
|
|
241
|
+
* failure is never treated as a drained queue: the initial read failure
|
|
242
|
+
* aborts the flush, and a final read failure reports a partial result rather
|
|
243
|
+
* than claiming all mutations were replayed. */
|
|
221
244
|
async function flushMutationQueue(): Promise<void> {
|
|
222
|
-
const
|
|
245
|
+
const read = await getQueuedMutations();
|
|
246
|
+
if (!read.ok) {
|
|
247
|
+
console.warn('Failed to read mutation queue:', read.error);
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
const mutations = read.mutations;
|
|
223
251
|
if (mutations.length === 0) return;
|
|
224
252
|
|
|
253
|
+
let replayed = 0;
|
|
225
254
|
for (const mut of mutations) {
|
|
226
255
|
try {
|
|
227
256
|
const opts: RequestInit = {
|
|
@@ -238,6 +267,7 @@ async function flushMutationQueue(): Promise<void> {
|
|
|
238
267
|
// avoid duplicate replays on the next flush; it will retry later.
|
|
239
268
|
break;
|
|
240
269
|
}
|
|
270
|
+
replayed++;
|
|
241
271
|
} else {
|
|
242
272
|
console.warn('Offline mutation failed:', mut.method, mut.path, res.status);
|
|
243
273
|
// Stop processing on first failure — try again later
|
|
@@ -250,8 +280,25 @@ async function flushMutationQueue(): Promise<void> {
|
|
|
250
280
|
}
|
|
251
281
|
|
|
252
282
|
// Notify clients about replayed mutations
|
|
253
|
-
const
|
|
283
|
+
const remainingRead = await getQueuedMutations();
|
|
254
284
|
const clients = await sw.clients.matchAll();
|
|
285
|
+
if (!remainingRead.ok) {
|
|
286
|
+
// Could not re-read the queue — do NOT claim all mutations were replayed.
|
|
287
|
+
// Report a partial result with the known replayed count so unreplayed
|
|
288
|
+
// mutations are not treated as drained.
|
|
289
|
+
console.warn('Failed to re-read mutation queue after replay:', remainingRead.error);
|
|
290
|
+
if (mutations.length > 0) {
|
|
291
|
+
clients.forEach((client) => {
|
|
292
|
+
client.postMessage({
|
|
293
|
+
type: 'MUTATIONS_PARTIAL',
|
|
294
|
+
replayed,
|
|
295
|
+
remaining: mutations.length - replayed,
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
const remaining = remainingRead.mutations;
|
|
255
302
|
if (remaining.length === 0 && mutations.length > 0) {
|
|
256
303
|
clients.forEach((client) => {
|
|
257
304
|
client.postMessage({ type: 'MUTATIONS_REPLAYED', count: mutations.length });
|
|
@@ -445,4 +492,21 @@ sw.addEventListener('sync', (event: Event) => {
|
|
|
445
492
|
// ── Online event: flush queue when connectivity returns ──
|
|
446
493
|
sw.addEventListener('online', () => {
|
|
447
494
|
void flushMutationQueue();
|
|
448
|
-
});
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
// ── Test-only: expose queue internals ──
|
|
498
|
+
// app.ts registers this file as a CLASSIC worker (`register('/sw.js', ...)`
|
|
499
|
+
// with no `type: 'module'`), so it cannot use `export` — a module-mode script
|
|
500
|
+
// fails to load under a classic registration, which would take offline support
|
|
501
|
+
// down. That leaves a global as the only seam a test can reach.
|
|
502
|
+
//
|
|
503
|
+
// It is inert in the browser because a classic service worker is the only
|
|
504
|
+
// script that ever runs in its own global scope: no page script, extension or
|
|
505
|
+
// import shares it, so nothing exists that could set the flag before this line
|
|
506
|
+
// executes. That invariant is the registration mode, not the flag name — if
|
|
507
|
+
// app.ts ever registers with `type: 'module'`, replace this with real exports
|
|
508
|
+
// rather than keeping both.
|
|
509
|
+
const __testGlobals = globalThis as unknown as Record<string, unknown>;
|
|
510
|
+
if (__testGlobals.__swTestHarness) {
|
|
511
|
+
__testGlobals.__swInternals = { getQueuedMutations, flushMutationQueue, queueMutation, clearMutation };
|
|
512
|
+
}
|
package/public/src/theme.js
CHANGED
|
@@ -20,6 +20,9 @@ const LABEL = {
|
|
|
20
20
|
light: 'Theme: light',
|
|
21
21
|
dark: 'Theme: dark',
|
|
22
22
|
};
|
|
23
|
+
/** Read the persisted theme choice from localStorage, defaulting to `auto`
|
|
24
|
+
* when no preference is stored or when localStorage is unavailable (e.g.
|
|
25
|
+
* private browsing). */
|
|
23
26
|
export function getStoredTheme() {
|
|
24
27
|
try {
|
|
25
28
|
const v = localStorage.getItem(STORAGE_KEY);
|
|
@@ -29,6 +32,9 @@ export function getStoredTheme() {
|
|
|
29
32
|
catch { /* localStorage may be unavailable (private mode) */ }
|
|
30
33
|
return 'auto';
|
|
31
34
|
}
|
|
35
|
+
/** Apply a theme to the page: set `data-theme` on `<html>`, update the toggle
|
|
36
|
+
* button's glyph/title, and sync the `<meta name="theme-color">` tag so the
|
|
37
|
+
* mobile browser chrome matches the chosen palette. */
|
|
32
38
|
export function applyTheme(theme) {
|
|
33
39
|
document.documentElement.setAttribute('data-theme', theme);
|
|
34
40
|
const btn = document.getElementById('theme-toggle');
|
|
@@ -50,6 +56,8 @@ export function applyTheme(theme) {
|
|
|
50
56
|
meta.setAttribute('content', color);
|
|
51
57
|
}
|
|
52
58
|
}
|
|
59
|
+
/** Persist a theme choice to localStorage and immediately apply it to the
|
|
60
|
+
* document. */
|
|
53
61
|
export function setTheme(theme) {
|
|
54
62
|
try {
|
|
55
63
|
localStorage.setItem(STORAGE_KEY, theme);
|
|
@@ -57,12 +65,16 @@ export function setTheme(theme) {
|
|
|
57
65
|
catch { /* ignore */ }
|
|
58
66
|
applyTheme(theme);
|
|
59
67
|
}
|
|
68
|
+
/** Advance to the next theme in the auto → light → dark order (wrapping
|
|
69
|
+
* around) and persist plus apply the new selection. */
|
|
60
70
|
export function cycleTheme() {
|
|
61
71
|
const current = getStoredTheme();
|
|
62
72
|
const next = ORDER[(ORDER.indexOf(current) + 1) % ORDER.length];
|
|
63
73
|
setTheme(next);
|
|
64
74
|
}
|
|
65
|
-
|
|
75
|
+
/** Apply the persisted (or default) theme early to avoid a flash of the wrong
|
|
76
|
+
* palette, then register a listener so `auto` mode tracks live OS theme
|
|
77
|
+
* changes. */
|
|
66
78
|
export function initTheme() {
|
|
67
79
|
applyTheme(getStoredTheme());
|
|
68
80
|
// When in auto mode, react to live OS theme changes.
|
package/public/src/theme.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"theme.js","sourceRoot":"","sources":["theme.ts"],"names":[],"mappings":"AAAA,kEAAkE;AAClE,yDAAyD;AACzD,kEAAkE;AAClE,EAAE;AACF,4EAA4E;AAC5E,8EAA8E;AAC9E,yEAAyE;AACzE,8EAA8E;AAC9E,yDAAyD;
|
|
1
|
+
{"version":3,"file":"theme.js","sourceRoot":"","sources":["theme.ts"],"names":[],"mappings":"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"}
|