@awesome-markdown/board-ui 0.0.0-stage → 1.6.0
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/README.md +76 -2
- package/dist/board/Board.d.ts +24 -0
- package/dist/board/Board.d.ts.map +1 -0
- package/dist/board/Board.js +166 -0
- package/dist/board/Board.js.map +1 -0
- package/dist/board/Cell.d.ts +23 -0
- package/dist/board/Cell.d.ts.map +1 -0
- package/dist/board/Cell.js +145 -0
- package/dist/board/Cell.js.map +1 -0
- package/dist/board/ColumnHeader.d.ts +18 -0
- package/dist/board/ColumnHeader.d.ts.map +1 -0
- package/dist/board/ColumnHeader.js +31 -0
- package/dist/board/ColumnHeader.js.map +1 -0
- package/dist/board/HomelessItemCard.d.ts +15 -0
- package/dist/board/HomelessItemCard.d.ts.map +1 -0
- package/dist/board/HomelessItemCard.js +36 -0
- package/dist/board/HomelessItemCard.js.map +1 -0
- package/dist/board/HomelessPanel.d.ts +17 -0
- package/dist/board/HomelessPanel.d.ts.map +1 -0
- package/dist/board/HomelessPanel.js +26 -0
- package/dist/board/HomelessPanel.js.map +1 -0
- package/dist/board/ItemCard.d.ts +21 -0
- package/dist/board/ItemCard.d.ts.map +1 -0
- package/dist/board/ItemCard.js +104 -0
- package/dist/board/ItemCard.js.map +1 -0
- package/dist/board/SwimlaneRow.d.ts +23 -0
- package/dist/board/SwimlaneRow.d.ts.map +1 -0
- package/dist/board/SwimlaneRow.js +30 -0
- package/dist/board/SwimlaneRow.js.map +1 -0
- package/dist/board/dnd/boardDnd.d.ts +15 -0
- package/dist/board/dnd/boardDnd.d.ts.map +1 -0
- package/dist/board/dnd/boardDnd.js +69 -0
- package/dist/board/dnd/boardDnd.js.map +1 -0
- package/dist/board/dnd/dragTypes.d.ts +24 -0
- package/dist/board/dnd/dragTypes.d.ts.map +1 -0
- package/dist/board/dnd/dragTypes.js +22 -0
- package/dist/board/dnd/dragTypes.js.map +1 -0
- package/dist/board/dnd/mutateDragDrop.d.ts +37 -0
- package/dist/board/dnd/mutateDragDrop.d.ts.map +1 -0
- package/dist/board/dnd/mutateDragDrop.js +116 -0
- package/dist/board/dnd/mutateDragDrop.js.map +1 -0
- package/dist/board/dnd/onDragEnd.d.ts +24 -0
- package/dist/board/dnd/onDragEnd.d.ts.map +1 -0
- package/dist/board/dnd/onDragEnd.js +47 -0
- package/dist/board/dnd/onDragEnd.js.map +1 -0
- package/dist/board/layout/AxisBehavior.d.ts +35 -0
- package/dist/board/layout/AxisBehavior.d.ts.map +1 -0
- package/dist/board/layout/AxisBehavior.js +82 -0
- package/dist/board/layout/AxisBehavior.js.map +1 -0
- package/dist/board/layout/AxisDrawer.d.ts +17 -0
- package/dist/board/layout/AxisDrawer.d.ts.map +1 -0
- package/dist/board/layout/AxisDrawer.js +40 -0
- package/dist/board/layout/AxisDrawer.js.map +1 -0
- package/dist/board/layout/BoardDrawer.d.ts +11 -0
- package/dist/board/layout/BoardDrawer.d.ts.map +1 -0
- package/dist/board/layout/BoardDrawer.js +27 -0
- package/dist/board/layout/BoardDrawer.js.map +1 -0
- package/dist/board/layout/Drawer.d.ts +19 -0
- package/dist/board/layout/Drawer.d.ts.map +1 -0
- package/dist/board/layout/Drawer.js +18 -0
- package/dist/board/layout/Drawer.js.map +1 -0
- package/dist/board/layout/FilterEditor.d.ts +12 -0
- package/dist/board/layout/FilterEditor.d.ts.map +1 -0
- package/dist/board/layout/FilterEditor.js +11 -0
- package/dist/board/layout/FilterEditor.js.map +1 -0
- package/dist/board/layout/InlineControls.d.ts +22 -0
- package/dist/board/layout/InlineControls.d.ts.map +1 -0
- package/dist/board/layout/InlineControls.js +39 -0
- package/dist/board/layout/InlineControls.js.map +1 -0
- package/dist/board/layout/SortableAxis.d.ts +28 -0
- package/dist/board/layout/SortableAxis.d.ts.map +1 -0
- package/dist/board/layout/SortableAxis.js +39 -0
- package/dist/board/layout/SortableAxis.js.map +1 -0
- package/dist/board/layout/axis-defaults.d.ts +22 -0
- package/dist/board/layout/axis-defaults.d.ts.map +1 -0
- package/dist/board/layout/axis-defaults.js +37 -0
- package/dist/board/layout/axis-defaults.js.map +1 -0
- package/dist/board/layout/filter-draft.d.ts +21 -0
- package/dist/board/layout/filter-draft.d.ts.map +1 -0
- package/dist/board/layout/filter-draft.js +110 -0
- package/dist/board/layout/filter-draft.js.map +1 -0
- package/dist/board/layout/useBoardLayout.d.ts +27 -0
- package/dist/board/layout/useBoardLayout.d.ts.map +1 -0
- package/dist/board/layout/useBoardLayout.js +92 -0
- package/dist/board/layout/useBoardLayout.js.map +1 -0
- package/dist/breadcrumb.d.ts +22 -0
- package/dist/breadcrumb.d.ts.map +1 -0
- package/dist/breadcrumb.js +15 -0
- package/dist/breadcrumb.js.map +1 -0
- package/dist/components/BoardAssigneeField.d.ts +10 -0
- package/dist/components/BoardAssigneeField.d.ts.map +1 -0
- package/dist/components/BoardAssigneeField.js +37 -0
- package/dist/components/BoardAssigneeField.js.map +1 -0
- package/dist/components/ConflictBanner.d.ts +12 -0
- package/dist/components/ConflictBanner.d.ts.map +1 -0
- package/dist/components/ConflictBanner.js +47 -0
- package/dist/components/ConflictBanner.js.map +1 -0
- package/dist/components/ConflictDiff.d.ts +13 -0
- package/dist/components/ConflictDiff.d.ts.map +1 -0
- package/dist/components/ConflictDiff.js +97 -0
- package/dist/components/ConflictDiff.js.map +1 -0
- package/dist/components/ConflictPanel.d.ts +16 -0
- package/dist/components/ConflictPanel.d.ts.map +1 -0
- package/dist/components/ConflictPanel.js +53 -0
- package/dist/components/ConflictPanel.js.map +1 -0
- package/dist/components/DeleteItemConfirmDialog.d.ts +14 -0
- package/dist/components/DeleteItemConfirmDialog.d.ts.map +1 -0
- package/dist/components/DeleteItemConfirmDialog.js +46 -0
- package/dist/components/DeleteItemConfirmDialog.js.map +1 -0
- package/dist/components/ItemEditorActions.d.ts +15 -0
- package/dist/components/ItemEditorActions.d.ts.map +1 -0
- package/dist/components/ItemEditorActions.js +48 -0
- package/dist/components/ItemEditorActions.js.map +1 -0
- package/dist/index.d.ts +32 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/derive-summary.d.ts +9 -0
- package/dist/lib/derive-summary.d.ts.map +1 -0
- package/dist/lib/derive-summary.js +27 -0
- package/dist/lib/derive-summary.js.map +1 -0
- package/dist/lib/item-board.d.ts +5 -0
- package/dist/lib/item-board.d.ts.map +1 -0
- package/dist/lib/item-board.js +14 -0
- package/dist/lib/item-board.js.map +1 -0
- package/dist/lib/property-display.d.ts +18 -0
- package/dist/lib/property-display.d.ts.map +1 -0
- package/dist/lib/property-display.js +81 -0
- package/dist/lib/property-display.js.map +1 -0
- package/dist/pages/BoardListPage.d.ts +9 -0
- package/dist/pages/BoardListPage.d.ts.map +1 -0
- package/dist/pages/BoardListPage.js +113 -0
- package/dist/pages/BoardListPage.js.map +1 -0
- package/dist/pages/BoardPage.d.ts +7 -0
- package/dist/pages/BoardPage.d.ts.map +1 -0
- package/dist/pages/BoardPage.js +37 -0
- package/dist/pages/BoardPage.js.map +1 -0
- package/dist/pages/ItemEditorPage.d.ts +3 -0
- package/dist/pages/ItemEditorPage.d.ts.map +1 -0
- package/dist/pages/ItemEditorPage.js +255 -0
- package/dist/pages/ItemEditorPage.js.map +1 -0
- package/dist/provider/ProviderContext.d.ts +17 -0
- package/dist/provider/ProviderContext.d.ts.map +1 -0
- package/dist/provider/ProviderContext.js +21 -0
- package/dist/provider/ProviderContext.js.map +1 -0
- package/dist/router/RouterAdapter.d.ts +43 -0
- package/dist/router/RouterAdapter.d.ts.map +1 -0
- package/dist/router/RouterAdapter.js +21 -0
- package/dist/router/RouterAdapter.js.map +1 -0
- package/dist/shell/SyncStatusDot.d.ts +8 -0
- package/dist/shell/SyncStatusDot.d.ts.map +1 -0
- package/dist/shell/SyncStatusDot.js +58 -0
- package/dist/shell/SyncStatusDot.js.map +1 -0
- package/dist/shell/ThemeToggle.d.ts +8 -0
- package/dist/shell/ThemeToggle.d.ts.map +1 -0
- package/dist/shell/ThemeToggle.js +21 -0
- package/dist/shell/ThemeToggle.js.map +1 -0
- package/dist/state/theme-store.d.ts +17 -0
- package/dist/state/theme-store.d.ts.map +1 -0
- package/dist/state/theme-store.js +58 -0
- package/dist/state/theme-store.js.map +1 -0
- package/dist/state/useBoardRender.d.ts +16 -0
- package/dist/state/useBoardRender.d.ts.map +1 -0
- package/dist/state/useBoardRender.js +53 -0
- package/dist/state/useBoardRender.js.map +1 -0
- package/dist/state/useProviderSubscribe.d.ts +22 -0
- package/dist/state/useProviderSubscribe.d.ts.map +1 -0
- package/dist/state/useProviderSubscribe.js +48 -0
- package/dist/state/useProviderSubscribe.js.map +1 -0
- package/dist/sync/conflict-api.d.ts +16 -0
- package/dist/sync/conflict-api.d.ts.map +1 -0
- package/dist/sync/conflict-api.js +35 -0
- package/dist/sync/conflict-api.js.map +1 -0
- package/dist/sync/conflict-store.d.ts +44 -0
- package/dist/sync/conflict-store.d.ts.map +1 -0
- package/dist/sync/conflict-store.js +159 -0
- package/dist/sync/conflict-store.js.map +1 -0
- package/fonts/InterTight-Medium.woff2 +0 -0
- package/fonts/InterTight-Regular.woff2 +0 -0
- package/fonts/JetBrainsMono-Medium.woff2 +0 -0
- package/fonts/JetBrainsMono-Regular.woff2 +0 -0
- package/fonts/LICENSE +14 -0
- package/package.json +58 -3
- package/styles.css +232 -0
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { useProvider } from '../provider/ProviderContext.js';
|
|
4
|
+
import { isHttpProvider } from '@awesome-markdown/provider-http';
|
|
5
|
+
/**
|
|
6
|
+
* Maps HTTP connection state to the three sync visual states:
|
|
7
|
+
* - clean (idle/online) → static --ink-muted dot
|
|
8
|
+
* - pulling (connecting/reconnecting) → pulsing --ink dot
|
|
9
|
+
* - dirty (offline) → solid --ink dot
|
|
10
|
+
*/
|
|
11
|
+
function stateToSync(s) {
|
|
12
|
+
if (s === 'connecting' || s === 'reconnecting')
|
|
13
|
+
return 'pulling';
|
|
14
|
+
if (s === 'offline')
|
|
15
|
+
return 'dirty';
|
|
16
|
+
return 'clean';
|
|
17
|
+
}
|
|
18
|
+
const SYNC_COLORS = {
|
|
19
|
+
clean: 'var(--ink-muted)',
|
|
20
|
+
pulling: 'var(--ink)',
|
|
21
|
+
dirty: 'var(--ink)',
|
|
22
|
+
};
|
|
23
|
+
const SYNC_LABELS = {
|
|
24
|
+
clean: 'Sync clean',
|
|
25
|
+
pulling: 'Syncing…',
|
|
26
|
+
dirty: 'Sync dirty — changes pending',
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* 6 px sync-status circle.
|
|
30
|
+
* No chromatic colors — grayscale tokens only per design spec.
|
|
31
|
+
* pulling state: pulsing animation.
|
|
32
|
+
*/
|
|
33
|
+
export function SyncStatusDot() {
|
|
34
|
+
const provider = useProvider();
|
|
35
|
+
const isHttp = isHttpProvider(provider);
|
|
36
|
+
const [state, setState] = useState(isHttp ? provider.getConnectionState() : null);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (!isHttpProvider(provider)) {
|
|
39
|
+
setState(null);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
setState(provider.getConnectionState());
|
|
43
|
+
const unsub = provider.onConnectionStateChange((s) => setState(s));
|
|
44
|
+
return unsub;
|
|
45
|
+
}, [provider]);
|
|
46
|
+
const syncState = state !== null ? stateToSync(state) : 'clean';
|
|
47
|
+
const isPulsing = syncState === 'pulling';
|
|
48
|
+
return (_jsx("span", { style: {
|
|
49
|
+
display: 'inline-block',
|
|
50
|
+
width: '6px',
|
|
51
|
+
height: '6px',
|
|
52
|
+
borderRadius: '50%',
|
|
53
|
+
background: SYNC_COLORS[syncState],
|
|
54
|
+
flexShrink: 0,
|
|
55
|
+
animation: isPulsing ? 'pulse 1.5s ease-in-out infinite' : undefined,
|
|
56
|
+
}, "aria-label": SYNC_LABELS[syncState], title: SYNC_LABELS[syncState], "data-testid": "sync-status-dot", "data-sync-state": syncState, "data-connection-state": state ?? 'n/a' }));
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=SyncStatusDot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SyncStatusDot.js","sourceRoot":"","sources":["../../src/shell/SyncStatusDot.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAGjE;;;;;GAKG;AACH,SAAS,WAAW,CAAC,CAAkB;IACrC,IAAI,CAAC,KAAK,YAAY,IAAI,CAAC,KAAK,cAAc;QAAE,OAAO,SAAS,CAAC;IACjE,IAAI,CAAC,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IACpC,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,WAAW,GAAkD;IACjE,KAAK,EAAE,kBAAkB;IACzB,OAAO,EAAE,YAAY;IACrB,KAAK,EAAE,YAAY;CACpB,CAAC;AAEF,MAAM,WAAW,GAAkD;IACjE,KAAK,EAAE,YAAY;IACnB,OAAO,EAAE,UAAU;IACnB,KAAK,EAAE,8BAA8B;CACtC,CAAC;AAEF;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,MAAM,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAExC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAChC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC,IAAI,CAC9C,CAAC;IAEF,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC9B,QAAQ,CAAC,IAAI,CAAC,CAAC;YACf,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,QAAQ,CAAC,kBAAkB,EAAE,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,uBAAuB,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,SAAS,GAAG,KAAK,KAAK,IAAI,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,MAAM,SAAS,GAAG,SAAS,KAAK,SAAS,CAAC;IAE1C,OAAO,CACL,eACE,KAAK,EAAE;YACL,OAAO,EAAE,cAAc;YACvB,KAAK,EAAE,KAAK;YACZ,MAAM,EAAE,KAAK;YACb,YAAY,EAAE,KAAK;YACnB,UAAU,EAAE,WAAW,CAAC,SAAS,CAAC;YAClC,UAAU,EAAE,CAAC;YACb,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,iCAAiC,CAAC,CAAC,CAAC,SAAS;SACrE,gBACW,WAAW,CAAC,SAAS,CAAC,EAClC,KAAK,EAAE,WAAW,CAAC,SAAS,CAAC,iBACjB,iBAAiB,qBACZ,SAAS,2BACH,KAAK,IAAI,KAAK,GACrC,CACH,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Theme toggle button — delegates to useTheme() from theme-store.
|
|
4
|
+
* ☀ = currently light (click to switch to dark)
|
|
5
|
+
* ☾ = currently dark (click to switch to light)
|
|
6
|
+
*/
|
|
7
|
+
export declare function ThemeToggle(): React.ReactElement;
|
|
8
|
+
//# sourceMappingURL=ThemeToggle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.d.ts","sourceRoot":"","sources":["../../src/shell/ThemeToggle.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAG1B;;;;GAIG;AACH,wBAAgB,WAAW,IAAI,KAAK,CAAC,YAAY,CAuBhD"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { useTheme } from '../state/theme-store.js';
|
|
4
|
+
/**
|
|
5
|
+
* Theme toggle button — delegates to useTheme() from theme-store.
|
|
6
|
+
* ☀ = currently light (click to switch to dark)
|
|
7
|
+
* ☾ = currently dark (click to switch to light)
|
|
8
|
+
*/
|
|
9
|
+
export function ThemeToggle() {
|
|
10
|
+
const { theme, toggle } = useTheme();
|
|
11
|
+
return (_jsx("button", { type: "button", onClick: toggle, "aria-label": theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode', title: theme === 'light' ? 'Switch to dark mode' : 'Switch to light mode', "data-testid": "theme-toggle", style: {
|
|
12
|
+
background: 'none',
|
|
13
|
+
border: 'none',
|
|
14
|
+
cursor: 'pointer',
|
|
15
|
+
color: 'var(--ink-muted)',
|
|
16
|
+
fontSize: '14px',
|
|
17
|
+
padding: '2px 4px',
|
|
18
|
+
lineHeight: 1,
|
|
19
|
+
}, children: theme === 'light' ? '☀' : '☾' }));
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=ThemeToggle.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ThemeToggle.js","sourceRoot":"","sources":["../../src/shell/ThemeToggle.tsx"],"names":[],"mappings":";AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;;;GAIG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC;IAErC,OAAO,CACL,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,MAAM,gBACH,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,sBAAsB,EAC9E,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,sBAAsB,iBAC7D,cAAc,EAC1B,KAAK,EAAE;YACL,UAAU,EAAE,MAAM;YAClB,MAAM,EAAE,MAAM;YACd,MAAM,EAAE,SAAS;YACjB,KAAK,EAAE,kBAAkB;YACzB,QAAQ,EAAE,MAAM;YAChB,OAAO,EAAE,SAAS;YAClB,UAAU,EAAE,CAAC;SACd,YAEA,KAAK,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,GACvB,CACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type Theme = 'light' | 'dark';
|
|
2
|
+
/**
|
|
3
|
+
* React hook for theme management.
|
|
4
|
+
*
|
|
5
|
+
* - Reads initial value from localStorage, falls back to `prefers-color-scheme`.
|
|
6
|
+
* - On change: writes `document.documentElement.dataset.theme` and localStorage.
|
|
7
|
+
* - localStorage unavailable → in-memory only, no error surfaced.
|
|
8
|
+
*
|
|
9
|
+
* UC-6: Theme toggles light↔dark, persists to localStorage, hydrates from OS pref.
|
|
10
|
+
*/
|
|
11
|
+
export declare function useTheme(): {
|
|
12
|
+
theme: Theme;
|
|
13
|
+
toggle(): void;
|
|
14
|
+
set(t: Theme): void;
|
|
15
|
+
};
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=theme-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-store.d.ts","sourceRoot":"","sources":["../../src/state/theme-store.ts"],"names":[],"mappings":"AAEA,KAAK,KAAK,GAAG,OAAO,GAAG,MAAM,CAAC;AAmC9B;;;;;;;;GAQG;AACH,wBAAgB,QAAQ,IAAI;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,IAAI,IAAI,CAAC;IAAC,GAAG,CAAC,CAAC,EAAE,KAAK,GAAG,IAAI,CAAA;CAAE,CAkBhF"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
|
2
|
+
/** Read the persisted theme from localStorage, or null if unavailable. */
|
|
3
|
+
function readStorage() {
|
|
4
|
+
try {
|
|
5
|
+
const stored = localStorage.getItem('theme');
|
|
6
|
+
if (stored === 'light' || stored === 'dark')
|
|
7
|
+
return stored;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
// localStorage unavailable (e.g. private browsing, security policy)
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
/** Write theme to localStorage; silently ignores storage errors. */
|
|
15
|
+
function writeStorage(theme) {
|
|
16
|
+
try {
|
|
17
|
+
localStorage.setItem('theme', theme);
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// unavailable — in-memory only
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/** Detect OS colour-scheme preference. */
|
|
24
|
+
function detectOsTheme() {
|
|
25
|
+
if (typeof window !== 'undefined' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
26
|
+
return 'dark';
|
|
27
|
+
}
|
|
28
|
+
return 'light';
|
|
29
|
+
}
|
|
30
|
+
/** Apply theme to the document root element. */
|
|
31
|
+
function applyTheme(theme) {
|
|
32
|
+
document.documentElement.dataset['theme'] = theme;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* React hook for theme management.
|
|
36
|
+
*
|
|
37
|
+
* - Reads initial value from localStorage, falls back to `prefers-color-scheme`.
|
|
38
|
+
* - On change: writes `document.documentElement.dataset.theme` and localStorage.
|
|
39
|
+
* - localStorage unavailable → in-memory only, no error surfaced.
|
|
40
|
+
*
|
|
41
|
+
* UC-6: Theme toggles light↔dark, persists to localStorage, hydrates from OS pref.
|
|
42
|
+
*/
|
|
43
|
+
export function useTheme() {
|
|
44
|
+
const [theme, setThemeState] = useState(() => readStorage() ?? detectOsTheme());
|
|
45
|
+
// Sync to DOM on mount and whenever theme changes
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
applyTheme(theme);
|
|
48
|
+
writeStorage(theme);
|
|
49
|
+
}, [theme]);
|
|
50
|
+
const toggle = useCallback(() => {
|
|
51
|
+
setThemeState((t) => (t === 'light' ? 'dark' : 'light'));
|
|
52
|
+
}, []);
|
|
53
|
+
const set = useCallback((t) => {
|
|
54
|
+
setThemeState(t);
|
|
55
|
+
}, []);
|
|
56
|
+
return { theme, toggle, set };
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=theme-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"theme-store.js","sourceRoot":"","sources":["../../src/state/theme-store.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAC;AAIzD,0EAA0E;AAC1E,SAAS,WAAW;IAClB,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAC7C,IAAI,MAAM,KAAK,OAAO,IAAI,MAAM,KAAK,MAAM;YAAE,OAAO,MAAM,CAAC;IAC7D,CAAC;IAAC,MAAM,CAAC;QACP,oEAAoE;IACtE,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,oEAAoE;AACpE,SAAS,YAAY,CAAC,KAAY;IAChC,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAAC,MAAM,CAAC;QACP,+BAA+B;IACjC,CAAC;AACH,CAAC;AAED,0CAA0C;AAC1C,SAAS,aAAa;IACpB,IAAI,OAAO,MAAM,KAAK,WAAW,IAAI,MAAM,CAAC,UAAU,CAAC,8BAA8B,CAAC,CAAC,OAAO,EAAE,CAAC;QAC/F,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,gDAAgD;AAChD,SAAS,UAAU,CAAC,KAAY;IAC9B,QAAQ,CAAC,eAAe,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC;AACpD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,QAAQ;IACtB,MAAM,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,QAAQ,CAAQ,GAAG,EAAE,CAAC,WAAW,EAAE,IAAI,aAAa,EAAE,CAAC,CAAC;IAEvF,kDAAkD;IAClD,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,KAAK,CAAC,CAAC;QAClB,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,MAAM,MAAM,GAAG,WAAW,CAAC,GAAS,EAAE;QACpC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;IAC3D,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,GAAG,GAAG,WAAW,CAAC,CAAC,CAAQ,EAAQ,EAAE;QACzC,aAAa,CAAC,CAAC,CAAC,CAAC;IACnB,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AAChC,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BoardRender, Homeless } from '@awesome-markdown/contracts';
|
|
2
|
+
export type BoardRenderStatus = 'loading' | 'ready' | 'error';
|
|
3
|
+
export interface BoardRenderState {
|
|
4
|
+
status: BoardRenderStatus;
|
|
5
|
+
render: BoardRender | null;
|
|
6
|
+
homeless: Homeless | null;
|
|
7
|
+
refetch: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Fetches the render envelope and homeless list for a board by slug.
|
|
11
|
+
*
|
|
12
|
+
* Coalesces rapid SSE change events into a single re-fetch per board
|
|
13
|
+
* via `useProviderSubscribe` (debounce ~100 ms).
|
|
14
|
+
*/
|
|
15
|
+
export declare function useBoardRender(slug: string): BoardRenderState;
|
|
16
|
+
//# sourceMappingURL=useBoardRender.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBoardRender.d.ts","sourceRoot":"","sources":["../../src/state/useBoardRender.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,6BAA6B,CAAC;AAIzE,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,OAAO,GAAG,OAAO,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,MAAM,EAAE,WAAW,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,gBAAgB,CA6C7D"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useRef } from 'react';
|
|
2
|
+
import { useProvider } from '../provider/ProviderContext.js';
|
|
3
|
+
import { useProviderSubscribe } from './useProviderSubscribe.js';
|
|
4
|
+
/**
|
|
5
|
+
* Fetches the render envelope and homeless list for a board by slug.
|
|
6
|
+
*
|
|
7
|
+
* Coalesces rapid SSE change events into a single re-fetch per board
|
|
8
|
+
* via `useProviderSubscribe` (debounce ~100 ms).
|
|
9
|
+
*/
|
|
10
|
+
export function useBoardRender(slug) {
|
|
11
|
+
const provider = useProvider();
|
|
12
|
+
const [render, setRender] = useState(null);
|
|
13
|
+
const [homeless, setHomeless] = useState(null);
|
|
14
|
+
const [status, setStatus] = useState('loading');
|
|
15
|
+
const slugRef = useRef(slug);
|
|
16
|
+
slugRef.current = slug;
|
|
17
|
+
const fetchAll = useCallback(async () => {
|
|
18
|
+
const currentSlug = slugRef.current;
|
|
19
|
+
if (currentSlug === '') {
|
|
20
|
+
setRender(null);
|
|
21
|
+
setHomeless(null);
|
|
22
|
+
setStatus('ready');
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const [renderResult, homelessResult] = await Promise.all([
|
|
27
|
+
provider.getBoardRender(currentSlug),
|
|
28
|
+
provider.getHomeless(currentSlug),
|
|
29
|
+
]);
|
|
30
|
+
// Discard if slug changed while inflight
|
|
31
|
+
if (slugRef.current !== currentSlug)
|
|
32
|
+
return;
|
|
33
|
+
setRender(renderResult);
|
|
34
|
+
setHomeless(homelessResult);
|
|
35
|
+
setStatus('ready');
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
console.error('Failed to load board render', currentSlug, err);
|
|
39
|
+
if (slugRef.current === currentSlug) {
|
|
40
|
+
setStatus('error');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, [provider]);
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
setRender(null);
|
|
46
|
+
setHomeless(null);
|
|
47
|
+
setStatus('loading');
|
|
48
|
+
void fetchAll();
|
|
49
|
+
}, [provider, slug, fetchAll]);
|
|
50
|
+
useProviderSubscribe(() => void fetchAll());
|
|
51
|
+
return { status, render, homeless, refetch: fetchAll };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=useBoardRender.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useBoardRender.js","sourceRoot":"","sources":["../../src/state/useBoardRender.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAEjE,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AAWjE;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAqB,IAAI,CAAC,CAAC;IAC/D,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAkB,IAAI,CAAC,CAAC;IAChE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAoB,SAAS,CAAC,CAAC;IAEnE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAEvB,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,IAAmB,EAAE;QACrD,MAAM,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC;QACpC,IAAI,WAAW,KAAK,EAAE,EAAE,CAAC;YACvB,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,SAAS,CAAC,OAAO,CAAC,CAAC;YACnB,OAAO;QACT,CAAC;QACD,IAAI,CAAC;YACH,MAAM,CAAC,YAAY,EAAE,cAAc,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;gBACvD,QAAQ,CAAC,cAAc,CAAC,WAAW,CAAC;gBACpC,QAAQ,CAAC,WAAW,CAAC,WAAW,CAAC;aAClC,CAAC,CAAC;YACH,yCAAyC;YACzC,IAAI,OAAO,CAAC,OAAO,KAAK,WAAW;gBAAE,OAAO;YAC5C,SAAS,CAAC,YAAY,CAAC,CAAC;YACxB,WAAW,CAAC,cAAc,CAAC,CAAC;YAC5B,SAAS,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,6BAA6B,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;YAC/D,IAAI,OAAO,CAAC,OAAO,KAAK,WAAW,EAAE,CAAC;gBACpC,SAAS,CAAC,OAAO,CAAC,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,SAAS,CAAC,GAAG,EAAE;QACb,SAAS,CAAC,IAAI,CAAC,CAAC;QAChB,WAAW,CAAC,IAAI,CAAC,CAAC;QAClB,SAAS,CAAC,SAAS,CAAC,CAAC;QACrB,KAAK,QAAQ,EAAE,CAAC;IAClB,CAAC,EAAE,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC;IAE/B,oBAAoB,CAAC,GAAG,EAAE,CAAC,KAAK,QAAQ,EAAE,CAAC,CAAC;IAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { ProviderEvent } from '@awesome-markdown/contracts';
|
|
2
|
+
type ChangeCallback = (event: ProviderEvent | null) => void;
|
|
3
|
+
/**
|
|
4
|
+
* Subscribes to all provider change signals and invokes `callback` on each,
|
|
5
|
+
* debounced by `debounceMs` (default 100 ms).
|
|
6
|
+
*
|
|
7
|
+
* Two signal sources are wired:
|
|
8
|
+
* - `provider.subscribe()` — fires when the http/localstorage provider detects
|
|
9
|
+
* a mutation (local API write or provider-fs file-watcher). The `ProviderEvent`
|
|
10
|
+
* payload is forwarded to the callback.
|
|
11
|
+
* - `window` `sync-engine:change` custom event — fired by `conflict-store` when
|
|
12
|
+
* the sync-engine SSE (port 7402) broadcasts a change (e.g. after a git pull).
|
|
13
|
+
* The callback receives `null` because this path carries no entity metadata.
|
|
14
|
+
*
|
|
15
|
+
* The callback is stabilised via `useRef` so callers do not need to memoize it —
|
|
16
|
+
* the latest closure is always called without re-registering subscriptions.
|
|
17
|
+
*
|
|
18
|
+
* Subscriptions are re-established when `provider` or `debounceMs` change.
|
|
19
|
+
*/
|
|
20
|
+
export declare function useProviderSubscribe(callback: ChangeCallback, debounceMs?: number): void;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=useProviderSubscribe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProviderSubscribe.d.ts","sourceRoot":"","sources":["../../src/state/useProviderSubscribe.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,KAAK,cAAc,GAAG,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,KAAK,IAAI,CAAC;AAE5D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,cAAc,EACxB,UAAU,SAAM,GACf,IAAI,CAgCN"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
import { useProvider } from '../provider/ProviderContext.js';
|
|
3
|
+
/**
|
|
4
|
+
* Subscribes to all provider change signals and invokes `callback` on each,
|
|
5
|
+
* debounced by `debounceMs` (default 100 ms).
|
|
6
|
+
*
|
|
7
|
+
* Two signal sources are wired:
|
|
8
|
+
* - `provider.subscribe()` — fires when the http/localstorage provider detects
|
|
9
|
+
* a mutation (local API write or provider-fs file-watcher). The `ProviderEvent`
|
|
10
|
+
* payload is forwarded to the callback.
|
|
11
|
+
* - `window` `sync-engine:change` custom event — fired by `conflict-store` when
|
|
12
|
+
* the sync-engine SSE (port 7402) broadcasts a change (e.g. after a git pull).
|
|
13
|
+
* The callback receives `null` because this path carries no entity metadata.
|
|
14
|
+
*
|
|
15
|
+
* The callback is stabilised via `useRef` so callers do not need to memoize it —
|
|
16
|
+
* the latest closure is always called without re-registering subscriptions.
|
|
17
|
+
*
|
|
18
|
+
* Subscriptions are re-established when `provider` or `debounceMs` change.
|
|
19
|
+
*/
|
|
20
|
+
export function useProviderSubscribe(callback, debounceMs = 100) {
|
|
21
|
+
const provider = useProvider();
|
|
22
|
+
// Always holds the latest callback without causing effect re-runs.
|
|
23
|
+
const callbackRef = useRef(callback);
|
|
24
|
+
callbackRef.current = callback;
|
|
25
|
+
const debounceRef = useRef(null);
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
const schedule = (event) => {
|
|
28
|
+
if (debounceRef.current !== null)
|
|
29
|
+
clearTimeout(debounceRef.current);
|
|
30
|
+
debounceRef.current = setTimeout(() => {
|
|
31
|
+
debounceRef.current = null;
|
|
32
|
+
callbackRef.current(event);
|
|
33
|
+
}, debounceMs);
|
|
34
|
+
};
|
|
35
|
+
const unsubscribe = provider.subscribe((event) => schedule(event));
|
|
36
|
+
const handleWindowChange = () => schedule(null);
|
|
37
|
+
window.addEventListener('sync-engine:change', handleWindowChange);
|
|
38
|
+
return () => {
|
|
39
|
+
unsubscribe();
|
|
40
|
+
window.removeEventListener('sync-engine:change', handleWindowChange);
|
|
41
|
+
if (debounceRef.current !== null) {
|
|
42
|
+
clearTimeout(debounceRef.current);
|
|
43
|
+
debounceRef.current = null;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}, [provider, debounceMs]);
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=useProviderSubscribe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useProviderSubscribe.js","sourceRoot":"","sources":["../../src/state/useProviderSubscribe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AAE1C,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAI7D;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,oBAAoB,CAClC,QAAwB,EACxB,UAAU,GAAG,GAAG;IAEhB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAE/B,mEAAmE;IACnE,MAAM,WAAW,GAAG,MAAM,CAAiB,QAAQ,CAAC,CAAC;IACrD,WAAW,CAAC,OAAO,GAAG,QAAQ,CAAC;IAE/B,MAAM,WAAW,GAAG,MAAM,CAAuC,IAAI,CAAC,CAAC;IAEvE,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,QAAQ,GAAG,CAAC,KAA2B,EAAQ,EAAE;YACrD,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI;gBAAE,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;YACpE,WAAW,CAAC,OAAO,GAAG,UAAU,CAAC,GAAG,EAAE;gBACpC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;gBAC3B,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;YAC7B,CAAC,EAAE,UAAU,CAAC,CAAC;QACjB,CAAC,CAAC;QAEF,MAAM,WAAW,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QAEnE,MAAM,kBAAkB,GAAG,GAAS,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,gBAAgB,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;QAElE,OAAO,GAAG,EAAE;YACV,WAAW,EAAE,CAAC;YACd,MAAM,CAAC,mBAAmB,CAAC,oBAAoB,EAAE,kBAAkB,CAAC,CAAC;YACrE,IAAI,WAAW,CAAC,OAAO,KAAK,IAAI,EAAE,CAAC;gBACjC,YAAY,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;gBAClC,WAAW,CAAC,OAAO,GAAG,IAAI,CAAC;YAC7B,CAAC;QACH,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC,CAAC;AAC7B,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { ConflictState, ResolveDecision, ResolveResponse } from '@awesome-markdown/contracts';
|
|
2
|
+
/**
|
|
3
|
+
* Fetch the current conflict state from the sync-engine.
|
|
4
|
+
*
|
|
5
|
+
* @param base Base URL of the sync-engine HTTP server (resolved by the host app).
|
|
6
|
+
*/
|
|
7
|
+
export declare function fetchConflictState(base: string): Promise<ConflictState | null>;
|
|
8
|
+
/**
|
|
9
|
+
* Submit resolution decisions to the sync-engine.
|
|
10
|
+
*
|
|
11
|
+
* @param base Base URL of the sync-engine HTTP server.
|
|
12
|
+
* @param mergeId The active mergeId.
|
|
13
|
+
* @param decisions Map of repo-relative path → decision.
|
|
14
|
+
*/
|
|
15
|
+
export declare function submitDecisions(base: string, mergeId: string, decisions: Record<string, ResolveDecision>): Promise<ResolveResponse>;
|
|
16
|
+
//# sourceMappingURL=conflict-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-api.d.ts","sourceRoot":"","sources":["../../src/sync/conflict-api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,eAAe,EACf,eAAe,EAChB,MAAM,6BAA6B,CAAC;AAErC;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAOpF;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,GACzC,OAAO,CAAC,eAAe,CAAC,CAe1B"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch the current conflict state from the sync-engine.
|
|
3
|
+
*
|
|
4
|
+
* @param base Base URL of the sync-engine HTTP server (resolved by the host app).
|
|
5
|
+
*/
|
|
6
|
+
export async function fetchConflictState(base) {
|
|
7
|
+
const resp = await fetch(`${base}/sync/conflict/state`, {
|
|
8
|
+
signal: AbortSignal.timeout(5_000),
|
|
9
|
+
});
|
|
10
|
+
if (!resp.ok)
|
|
11
|
+
return null;
|
|
12
|
+
const body = (await resp.json());
|
|
13
|
+
return body.conflict ?? null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Submit resolution decisions to the sync-engine.
|
|
17
|
+
*
|
|
18
|
+
* @param base Base URL of the sync-engine HTTP server.
|
|
19
|
+
* @param mergeId The active mergeId.
|
|
20
|
+
* @param decisions Map of repo-relative path → decision.
|
|
21
|
+
*/
|
|
22
|
+
export async function submitDecisions(base, mergeId, decisions) {
|
|
23
|
+
const resp = await fetch(`${base}/sync/conflict/resolve`, {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify({ mergeId, decisions }),
|
|
27
|
+
signal: AbortSignal.timeout(15_000),
|
|
28
|
+
});
|
|
29
|
+
if (!resp.ok) {
|
|
30
|
+
const err = (await resp.json().catch(() => ({ message: resp.statusText })));
|
|
31
|
+
throw new Error(err.message ?? err.error ?? `HTTP ${resp.status}`);
|
|
32
|
+
}
|
|
33
|
+
return resp.json();
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=conflict-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-api.js","sourceRoot":"","sources":["../../src/sync/conflict-api.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,IAAY;IACnD,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,sBAAsB,EAAE;QACtD,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,KAAK,CAAC;KACnC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,OAAO,IAAI,CAAC;IAC1B,MAAM,IAAI,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAuC,CAAC;IACvE,OAAO,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC;AAC/B,CAAC;AAED;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,IAAY,EACZ,OAAe,EACf,SAA0C;IAE1C,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,wBAAwB,EAAE;QACxD,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;QAC5C,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,MAAM,CAAC;KACpC,CAAC,CAAC;IACH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACb,MAAM,GAAG,GAAG,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAGzE,CAAC;QACF,MAAM,IAAI,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,KAAK,IAAI,QAAQ,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,EAA8B,CAAC;AACjD,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { ConflictState, ResolveDecision } from '@awesome-markdown/contracts';
|
|
3
|
+
export interface ConflictStoreState {
|
|
4
|
+
activeConflict: ConflictState | null;
|
|
5
|
+
submitting: boolean;
|
|
6
|
+
error: string | null;
|
|
7
|
+
}
|
|
8
|
+
interface ConflictContextValue extends ConflictStoreState {
|
|
9
|
+
isPathAffected: (filePath: string) => boolean;
|
|
10
|
+
/** Returns true if any conflicted path contains the given item ID. */
|
|
11
|
+
isItemAffected: (itemId: string) => boolean;
|
|
12
|
+
decisionFor: (filePath: string) => ResolveDecision | null;
|
|
13
|
+
resolve: (decisions: Record<string, ResolveDecision>) => Promise<void>;
|
|
14
|
+
dismissError: () => void;
|
|
15
|
+
}
|
|
16
|
+
interface ConflictProviderProps {
|
|
17
|
+
/**
|
|
18
|
+
* Base URL of the sync-engine HTTP server (e.g. `http://localhost:7402` or a
|
|
19
|
+
* same-origin proxy path such as `/sync-engine`). Hosts without a
|
|
20
|
+
* sync-engine simply don't mount `ConflictProvider`.
|
|
21
|
+
*/
|
|
22
|
+
syncEngineUrl: string;
|
|
23
|
+
children: React.ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Provides the conflict store to the component tree.
|
|
27
|
+
*
|
|
28
|
+
* On mount:
|
|
29
|
+
* 1. Fetches GET /sync/conflict/state to hydrate any in-flight conflict.
|
|
30
|
+
* 2. Subscribes to SSE events (conflict / synced) via EventSource.
|
|
31
|
+
*
|
|
32
|
+
* The EventSource is reconnect-safe: duplicate `conflict` events for the same
|
|
33
|
+
* mergeId are coalesced (idempotent set-state).
|
|
34
|
+
*/
|
|
35
|
+
export declare function ConflictProvider({ syncEngineUrl, children, }: ConflictProviderProps): React.ReactElement;
|
|
36
|
+
/** Access the conflict store. Must be used inside <ConflictProvider>. */
|
|
37
|
+
export declare function useConflict(): ConflictContextValue;
|
|
38
|
+
/**
|
|
39
|
+
* Like useConflict but returns null when not inside <ConflictProvider>.
|
|
40
|
+
* Useful for components that are also rendered outside a provider (e.g. tests).
|
|
41
|
+
*/
|
|
42
|
+
export declare function useOptionalConflict(): ConflictContextValue | null;
|
|
43
|
+
export {};
|
|
44
|
+
//# sourceMappingURL=conflict-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-store.d.ts","sourceRoot":"","sources":["../../src/sync/conflict-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAON,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAOlF,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,aAAa,GAAG,IAAI,CAAC;IACrC,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;CACtB;AAwCD,UAAU,oBAAqB,SAAQ,kBAAkB;IACvD,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC;IAC9C,sEAAsE;IACtE,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC;IAC5C,WAAW,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,eAAe,GAAG,IAAI,CAAC;IAC1D,OAAO,EAAE,CAAC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvE,YAAY,EAAE,MAAM,IAAI,CAAC;CAC1B;AAQD,UAAU,qBAAqB;IAC7B;;;;OAIG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,aAAa,EACb,QAAQ,GACT,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CAgI5C;AAED,yEAAyE;AACzE,wBAAgB,WAAW,IAAI,oBAAoB,CAMlD;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,oBAAoB,GAAG,IAAI,CAEjE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import React, { createContext, useContext, useReducer, useEffect, useCallback, useRef, } from 'react';
|
|
2
|
+
import { fetchConflictState, submitDecisions } from './conflict-api.js';
|
|
3
|
+
const initialState = {
|
|
4
|
+
activeConflict: null,
|
|
5
|
+
submitting: false,
|
|
6
|
+
error: null,
|
|
7
|
+
};
|
|
8
|
+
function conflictReducer(state, action) {
|
|
9
|
+
switch (action.type) {
|
|
10
|
+
case 'SET_CONFLICT':
|
|
11
|
+
return { ...state, activeConflict: action.conflict, error: null };
|
|
12
|
+
case 'CLEAR_CONFLICT':
|
|
13
|
+
return { ...state, activeConflict: null, submitting: false, error: null };
|
|
14
|
+
case 'SET_SUBMITTING':
|
|
15
|
+
return { ...state, submitting: action.value };
|
|
16
|
+
case 'SET_ERROR':
|
|
17
|
+
return { ...state, error: action.message, submitting: false };
|
|
18
|
+
default:
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
const ConflictCtx = createContext(null);
|
|
23
|
+
/**
|
|
24
|
+
* Provides the conflict store to the component tree.
|
|
25
|
+
*
|
|
26
|
+
* On mount:
|
|
27
|
+
* 1. Fetches GET /sync/conflict/state to hydrate any in-flight conflict.
|
|
28
|
+
* 2. Subscribes to SSE events (conflict / synced) via EventSource.
|
|
29
|
+
*
|
|
30
|
+
* The EventSource is reconnect-safe: duplicate `conflict` events for the same
|
|
31
|
+
* mergeId are coalesced (idempotent set-state).
|
|
32
|
+
*/
|
|
33
|
+
export function ConflictProvider({ syncEngineUrl, children, }) {
|
|
34
|
+
const [state, dispatch] = useReducer(conflictReducer, initialState);
|
|
35
|
+
const esRef = useRef(null);
|
|
36
|
+
// Hydrate from server on mount
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
let cancelled = false;
|
|
39
|
+
fetchConflictState(syncEngineUrl).then((conflict) => {
|
|
40
|
+
if (!cancelled) {
|
|
41
|
+
dispatch({ type: 'SET_CONFLICT', conflict });
|
|
42
|
+
}
|
|
43
|
+
}).catch(() => {
|
|
44
|
+
// Sync-engine not running — ignore silently
|
|
45
|
+
});
|
|
46
|
+
return () => { cancelled = true; };
|
|
47
|
+
}, [syncEngineUrl]);
|
|
48
|
+
// Subscribe to SSE events
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
let cancelled = false;
|
|
51
|
+
const base = syncEngineUrl;
|
|
52
|
+
// Defer EventSource creation past React StrictMode's synchronous
|
|
53
|
+
// cleanup pass so the connection isn't opened then immediately aborted.
|
|
54
|
+
const timer = setTimeout(() => {
|
|
55
|
+
if (cancelled)
|
|
56
|
+
return;
|
|
57
|
+
const es = new EventSource(`${base}/events`);
|
|
58
|
+
esRef.current = es;
|
|
59
|
+
es.addEventListener('conflict', (e) => {
|
|
60
|
+
try {
|
|
61
|
+
const data = JSON.parse(e.data);
|
|
62
|
+
// If same mergeId, coalesce (don't re-render unnecessarily)
|
|
63
|
+
// Fetch fresh state so we have the full ConflictState shape
|
|
64
|
+
fetchConflictState(syncEngineUrl).then((conflict) => {
|
|
65
|
+
dispatch({ type: 'SET_CONFLICT', conflict });
|
|
66
|
+
}).catch(() => { });
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Malformed event — ignore
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
es.addEventListener('synced', () => {
|
|
73
|
+
dispatch({ type: 'CLEAR_CONFLICT' });
|
|
74
|
+
});
|
|
75
|
+
es.addEventListener('change', () => {
|
|
76
|
+
window.dispatchEvent(new CustomEvent('sync-engine:change'));
|
|
77
|
+
});
|
|
78
|
+
}, 0);
|
|
79
|
+
// Clean up on unmount
|
|
80
|
+
return () => {
|
|
81
|
+
cancelled = true;
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
if (esRef.current) {
|
|
84
|
+
esRef.current.close();
|
|
85
|
+
esRef.current = null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
}, [syncEngineUrl]);
|
|
89
|
+
const isPathAffected = useCallback((filePath) => {
|
|
90
|
+
if (!state.activeConflict)
|
|
91
|
+
return false;
|
|
92
|
+
return state.activeConflict.pendingPaths.includes(filePath) ||
|
|
93
|
+
state.activeConflict.paths.some((p) => p.path === filePath);
|
|
94
|
+
}, [state.activeConflict]);
|
|
95
|
+
const isItemAffected = useCallback((itemId) => {
|
|
96
|
+
if (!state.activeConflict)
|
|
97
|
+
return false;
|
|
98
|
+
return state.activeConflict.paths.some((p) => p.path.includes(itemId));
|
|
99
|
+
}, [state.activeConflict]);
|
|
100
|
+
const decisionFor = useCallback((filePath) => {
|
|
101
|
+
if (!state.activeConflict)
|
|
102
|
+
return null;
|
|
103
|
+
const entry = state.activeConflict.paths.find((p) => p.path === filePath);
|
|
104
|
+
return entry?.decision ?? null;
|
|
105
|
+
}, [state.activeConflict]);
|
|
106
|
+
const resolve = useCallback(async (decisions) => {
|
|
107
|
+
if (!state.activeConflict)
|
|
108
|
+
return;
|
|
109
|
+
dispatch({ type: 'SET_SUBMITTING', value: true });
|
|
110
|
+
try {
|
|
111
|
+
const result = await submitDecisions(syncEngineUrl, state.activeConflict.mergeId, decisions);
|
|
112
|
+
if (result.status === 'completed') {
|
|
113
|
+
dispatch({ type: 'CLEAR_CONFLICT' });
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Partial — refresh state
|
|
117
|
+
const updated = await fetchConflictState(syncEngineUrl);
|
|
118
|
+
dispatch({ type: 'SET_CONFLICT', conflict: updated });
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (err) {
|
|
122
|
+
dispatch({
|
|
123
|
+
type: 'SET_ERROR',
|
|
124
|
+
message: err instanceof Error ? err.message : 'Resolution failed',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
finally {
|
|
128
|
+
dispatch({ type: 'SET_SUBMITTING', value: false });
|
|
129
|
+
}
|
|
130
|
+
}, [state.activeConflict, syncEngineUrl]);
|
|
131
|
+
const dismissError = useCallback(() => {
|
|
132
|
+
dispatch({ type: 'SET_ERROR', message: null });
|
|
133
|
+
}, []);
|
|
134
|
+
const value = {
|
|
135
|
+
...state,
|
|
136
|
+
isPathAffected,
|
|
137
|
+
isItemAffected,
|
|
138
|
+
decisionFor,
|
|
139
|
+
resolve,
|
|
140
|
+
dismissError,
|
|
141
|
+
};
|
|
142
|
+
return React.createElement(ConflictCtx.Provider, { value }, children);
|
|
143
|
+
}
|
|
144
|
+
/** Access the conflict store. Must be used inside <ConflictProvider>. */
|
|
145
|
+
export function useConflict() {
|
|
146
|
+
const ctx = useContext(ConflictCtx);
|
|
147
|
+
if (!ctx) {
|
|
148
|
+
throw new Error('useConflict must be used inside <ConflictProvider>');
|
|
149
|
+
}
|
|
150
|
+
return ctx;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Like useConflict but returns null when not inside <ConflictProvider>.
|
|
154
|
+
* Useful for components that are also rendered outside a provider (e.g. tests).
|
|
155
|
+
*/
|
|
156
|
+
export function useOptionalConflict() {
|
|
157
|
+
return useContext(ConflictCtx);
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=conflict-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflict-store.js","sourceRoot":"","sources":["../../src/sync/conflict-store.ts"],"names":[],"mappings":"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"}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|