@mdbase-dev/ui 0.1.0-beta.126 → 0.1.0-beta.148

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 (74) hide show
  1. package/README.md +168 -0
  2. package/css/collections.css +978 -0
  3. package/css/controls.css +33 -1
  4. package/css/screens.css +44 -2
  5. package/dist/AppSwitcher.js +187 -33
  6. package/dist/Checkbox.d.ts +14 -0
  7. package/dist/Checkbox.js +32 -0
  8. package/dist/CollectionModeControl.d.ts +11 -0
  9. package/dist/CollectionModeControl.js +290 -0
  10. package/dist/CommandPalette.d.ts +4 -2
  11. package/dist/CommandPalette.js +275 -103
  12. package/dist/Dialog.js +72 -25
  13. package/dist/Feedback.js +763 -229
  14. package/dist/FeedbackMarkup.d.ts +1 -1
  15. package/dist/FeedbackMarkup.js +326 -125
  16. package/dist/FeedbackVerification.js +115 -68
  17. package/dist/SaveNotice.js +71 -28
  18. package/dist/Select.js +183 -135
  19. package/dist/SelectList.js +59 -14
  20. package/dist/ThemeSelect.js +55 -4
  21. package/dist/apps.d.ts +1 -0
  22. package/dist/apps.js +50 -29
  23. package/dist/brand.js +251 -68
  24. package/dist/codemirror.js +116 -87
  25. package/dist/collections/CollectionGate.d.ts +52 -0
  26. package/dist/collections/CollectionGate.js +456 -0
  27. package/dist/collections/CollectionSaveWarning.d.ts +10 -0
  28. package/dist/collections/CollectionSaveWarning.js +28 -0
  29. package/dist/collections/CollectionSwitcher.d.ts +51 -0
  30. package/dist/collections/CollectionSwitcher.js +598 -0
  31. package/dist/collections/SetupReview.d.ts +9 -0
  32. package/dist/collections/SetupReview.js +216 -0
  33. package/dist/collections/copy.d.ts +100 -0
  34. package/dist/collections/copy.js +937 -0
  35. package/dist/collections/dialogs.d.ts +27 -0
  36. package/dist/collections/dialogs.js +169 -0
  37. package/dist/collections/model.d.ts +146 -0
  38. package/dist/collections/model.js +337 -0
  39. package/dist/collections/mount.d.ts +19 -0
  40. package/dist/collections/mount.js +56 -0
  41. package/dist/collections/parts.d.ts +86 -0
  42. package/dist/collections/parts.js +439 -0
  43. package/dist/collections/testing.d.ts +19 -0
  44. package/dist/collections/testing.js +39 -0
  45. package/dist/collections/use-collection-ui.d.ts +140 -0
  46. package/dist/collections/use-collection-ui.js +236 -0
  47. package/dist/collections.d.ts +19 -0
  48. package/dist/collections.js +13 -0
  49. package/dist/feedback-data.d.ts +13 -12
  50. package/dist/feedback-data.js +365 -168
  51. package/dist/flock.d.ts +63 -0
  52. package/dist/flock.js +140 -0
  53. package/dist/i18n.d.ts +6 -0
  54. package/dist/i18n.js +71 -0
  55. package/dist/locales/de/messages.json +1 -0
  56. package/dist/locales/en/messages.json +1 -0
  57. package/dist/locales/es/messages.json +1 -0
  58. package/dist/locales/fr/messages.json +1 -0
  59. package/dist/locales/it/messages.json +1 -0
  60. package/dist/locales/ja/messages.json +1 -0
  61. package/dist/locales/ko/messages.json +1 -0
  62. package/dist/locales/pt/messages.json +1 -0
  63. package/dist/locales/ru/messages.json +1 -0
  64. package/dist/locales/zh/messages.json +1 -0
  65. package/dist/mark-activity.js +82 -75
  66. package/dist/permission-selection.js +27 -17
  67. package/dist/popover.js +138 -130
  68. package/dist/screens.d.ts +20 -3
  69. package/dist/screens.js +275 -6
  70. package/dist/select-model.js +34 -28
  71. package/dist/text-diff.d.ts +1 -0
  72. package/dist/text-diff.js +106 -74
  73. package/dist/theme.js +34 -40
  74. package/package.json +56 -7
@@ -0,0 +1,290 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from "react";
3
+ import { i18n, useLocaleRevision } from "./i18n.js";
4
+ function read(value) {
5
+ if (!value || typeof value !== "object") return null;
6
+ const v = value;
7
+ if (v.execution_available !== true || v.current_mode !== "private" && v.current_mode !== "cloud_copy") return null;
8
+ if (v.change !== null) {
9
+ if (!v.change || typeof v.change !== "object") return null;
10
+ const c = v.change;
11
+ if (c.target_mode !== "private" && c.target_mode !== "cloud_copy" || typeof c.phase !== "string") return null;
12
+ }
13
+ return v;
14
+ }
15
+ /** Owner controls only. Visibility follows actual executor registration, not a
16
+ * mode flag. Browser intent cannot produce native key/source consent. */
17
+ export function CollectionModeControl({
18
+ collectionId,
19
+ load,
20
+ change,
21
+ cancel,
22
+ native = false
23
+ }) {
24
+ useLocaleRevision();
25
+ const [status, setStatus] = useState(null);
26
+ const [busy, setBusy] = useState(false);
27
+ const [failure, setFailure] = useState(false);
28
+ const [step, setStep] = useState(null);
29
+ const intent = useRef(null);
30
+ const generation = useRef(0);
31
+ // Callback identity changes with parent renders; it must not restart I/O.
32
+ const callbacks = useRef({
33
+ load,
34
+ change,
35
+ cancel
36
+ });
37
+ callbacks.current = {
38
+ load,
39
+ change,
40
+ cancel
41
+ };
42
+ useEffect(() => {
43
+ const active = ++generation.current;
44
+ setStatus(null);
45
+ setStep(null);
46
+ setFailure(false);
47
+ setBusy(false);
48
+ intent.current = null;
49
+ void callbacks.current.load().then(value => {
50
+ if (generation.current === active) setStatus(read(value));
51
+ }).catch(() => {});
52
+ return () => {
53
+ ++generation.current;
54
+ };
55
+ }, [collectionId]);
56
+ async function refresh() {
57
+ const active = generation.current;
58
+ setBusy(true);
59
+ setFailure(false);
60
+ try {
61
+ const next = read(await callbacks.current.load());
62
+ if (active !== generation.current) return;
63
+ setStatus(next);
64
+ if (next && !next.change) {
65
+ setStep(null);
66
+ intent.current = null;
67
+ }
68
+ } catch {
69
+ if (active === generation.current) setFailure(true);
70
+ } finally {
71
+ if (active === generation.current) setBusy(false);
72
+ }
73
+ }
74
+ async function commit(target) {
75
+ const active = generation.current;
76
+ if (intent.current?.target !== target) intent.current = {
77
+ target,
78
+ id: crypto.randomUUID()
79
+ };
80
+ setBusy(true);
81
+ setFailure(false);
82
+ try {
83
+ const result = await callbacks.current.change(target, intent.current.id);
84
+ if (active !== generation.current) return;
85
+ const next = read(await callbacks.current.load());
86
+ if (active !== generation.current) return;
87
+ setStatus(next);
88
+ if (next && !next.change) intent.current = null;
89
+ setStep(result && typeof result === "object" ? result : null);
90
+ } catch {
91
+ if (active === generation.current) setFailure(true);
92
+ } finally {
93
+ if (active === generation.current) setBusy(false);
94
+ }
95
+ }
96
+ async function cancelPending() {
97
+ if (!status?.change?.request_id || !callbacks.current.cancel) return;
98
+ const active = generation.current;
99
+ setBusy(true);
100
+ setFailure(false);
101
+ try {
102
+ await callbacks.current.cancel(status.change.target_mode, status.change.request_id);
103
+ const next = read(await callbacks.current.load());
104
+ if (active !== generation.current) return;
105
+ setStatus(next);
106
+ setStep(null);
107
+ intent.current = null;
108
+ } catch {
109
+ if (active === generation.current) setFailure(true);
110
+ } finally {
111
+ if (active === generation.current) setBusy(false);
112
+ }
113
+ }
114
+ if (!status) return null;
115
+ const pending = status.change;
116
+ const target = pending?.target_mode ?? (status.current_mode === "private" ? "cloud_copy" : "private");
117
+ const blocked = step?.phase === "waiting_for_devices" && Array.isArray(step.devices) ? step.devices.filter(v => typeof v === "string") : [];
118
+ const retryAt = typeof step?.retry_at === "string" ? step.retry_at : typeof pending?.retry_at === "string" ? pending.retry_at : undefined;
119
+ return _jsxs("section", {
120
+ className: "mdbase-collection-mode",
121
+ "aria-label": i18n._(
122
+ /** i18n */
123
+ {
124
+ id: "4GiuPT",
125
+ message: "Collection storage mode",
126
+ context: "Collection storage mode"
127
+ }),
128
+ "aria-busy": busy,
129
+ children: [_jsx("h3", {
130
+ children: i18n._(
131
+ /** i18n */
132
+ {
133
+ id: "vAr5Dp",
134
+ message: "Storage mode",
135
+ context: "Collection storage mode"
136
+ })
137
+ }), _jsx("p", {
138
+ children: status.current_mode === "private" ? i18n._(
139
+ /** i18n */
140
+ {
141
+ id: "EIDHh1",
142
+ message: "Private",
143
+ context: "Collection storage mode"
144
+ }) : i18n._(
145
+ /** i18n */
146
+ {
147
+ id: "oKQdii",
148
+ message: "Cloud copy",
149
+ context: "Collection storage mode"
150
+ })
151
+ }), _jsx("p", {
152
+ children: target === "private" ? i18n._(
153
+ /** i18n */
154
+ {
155
+ id: "FHz4md",
156
+ message: "Only your devices can read new content after the switch finishes. mdbase may still be able to read older content.",
157
+ context: "Collection storage mode"
158
+ }) : i18n._(
159
+ /** i18n */
160
+ {
161
+ id: "bcDV8T",
162
+ message: "mdbase will be able to read the collection, including its existing history, and serve it while your devices are off.",
163
+ context: "Collection storage mode"
164
+ })
165
+ }), pending && _jsx("p", {
166
+ role: "status",
167
+ children: blocked.length > 0 ? i18n._(
168
+ /** i18n */
169
+ {
170
+ id: "3w1RWH",
171
+ message: "Waiting for devices to update.",
172
+ context: "Collection storage mode"
173
+ }) : pending.phase === "waiting_for_token_expiry" ? i18n._(
174
+ /** i18n */
175
+ {
176
+ id: "lx5IrT",
177
+ message: "Waiting for older device connections to expire. Cloud copy remains on until the switch finishes.",
178
+ context: "Collection storage mode"
179
+ }) : pending.phase === "waiting_for_device" ? i18n._(
180
+ /** i18n */
181
+ {
182
+ id: "3FYCb9",
183
+ message: "Open mdbase connect on one of the owner's devices to finish the switch.",
184
+ context: "Collection storage mode"
185
+ }) : i18n._(
186
+ /** i18n */
187
+ {
188
+ id: "13DGsZ",
189
+ message: "Finishing the switch. Keep the owner device connected.",
190
+ context: "Collection storage mode"
191
+ })
192
+ }), retryAt && Number.isFinite(Date.parse(retryAt)) && _jsx("p", {
193
+ children: _jsx("time", {
194
+ dateTime: retryAt,
195
+ children: i18n._(
196
+ /** i18n */
197
+ {
198
+ id: "ejJPcY",
199
+ message: "Continue after {0}.",
200
+ context: "Collection storage mode",
201
+ values: {
202
+ 0: new Date(retryAt).toLocaleString()
203
+ }
204
+ })
205
+ })
206
+ }), blocked.length > 0 && _jsxs("div", {
207
+ role: "status",
208
+ children: [_jsx("p", {
209
+ children: i18n._(
210
+ /** i18n */
211
+ {
212
+ id: "YQ2gus",
213
+ message: "Update or revoke these devices before continuing:",
214
+ context: "Collection storage mode"
215
+ })
216
+ }), _jsx("ul", {
217
+ children: blocked.map(id => _jsx("li", {
218
+ children: _jsx("code", {
219
+ children: id
220
+ })
221
+ }, id))
222
+ })]
223
+ }), failure && _jsx("p", {
224
+ role: "alert",
225
+ children: i18n._(
226
+ /** i18n */
227
+ {
228
+ id: "Iu3ZPh",
229
+ message: "We couldn\u2019t confirm progress. Refresh, then retry. An interrupted switch may already have started.",
230
+ context: "Collection storage mode"
231
+ })
232
+ }), _jsxs("div", {
233
+ children: [(!pending || native) && _jsx("button", {
234
+ type: "button",
235
+ className: "mdbase-button",
236
+ disabled: busy,
237
+ onClick: () => void commit(target),
238
+ children: busy ? i18n._(
239
+ /** i18n */
240
+ {
241
+ id: "3xE-Bk",
242
+ message: "Working\u2026",
243
+ context: "Collection storage mode"
244
+ }) : pending ? i18n._(
245
+ /** i18n */
246
+ {
247
+ id: "JKLDUc",
248
+ message: "Continue on this device",
249
+ context: "Collection storage mode"
250
+ }) : target === "private" ? i18n._(
251
+ /** i18n */
252
+ {
253
+ id: "rO1a9-",
254
+ message: "Make private",
255
+ context: "Collection storage mode"
256
+ }) : i18n._(
257
+ /** i18n */
258
+ {
259
+ id: "nVYD2-",
260
+ message: "Turn on Cloud copy",
261
+ context: "Collection storage mode"
262
+ })
263
+ }), cancel && pending?.request_id && ["waiting_for_device", "waiting_for_token_expiry"].includes(pending.phase) && _jsx("button", {
264
+ type: "button",
265
+ className: "mdbase-button is-tertiary",
266
+ disabled: busy,
267
+ onClick: () => void cancelPending(),
268
+ children: i18n._(
269
+ /** i18n */
270
+ {
271
+ id: "VON31G",
272
+ message: "Cancel switch",
273
+ context: "Collection storage mode"
274
+ })
275
+ }), _jsx("button", {
276
+ type: "button",
277
+ className: "mdbase-button is-tertiary",
278
+ disabled: busy,
279
+ onClick: () => void refresh(),
280
+ children: i18n._(
281
+ /** i18n */
282
+ {
283
+ id: "tHHSXx",
284
+ message: "Refresh status",
285
+ context: "Collection storage mode"
286
+ })
287
+ })]
288
+ })]
289
+ });
290
+ }
@@ -3,7 +3,7 @@ export interface Command {
3
3
  readonly id: string;
4
4
  readonly label: string;
5
5
  readonly detail?: string | undefined;
6
- /** Commands show under their group's heading, in the order the groups first appear. */
6
+ /** Stable group ID; headings follow first appearance and may be presented by groupLabel. */
7
7
  readonly group: string;
8
8
  readonly keywords?: string | undefined;
9
9
  /** For example "mod+shift+f"; see shortcutLabel. */
@@ -27,11 +27,13 @@ export declare function matchingCommands(commands: readonly Command[], query: st
27
27
  * Find or run anything: every mdbase app opens it with Mod+K. Typing filters commands; the
28
28
  * arrows move, Enter runs, Shift+Enter runs a command's alternate, Escape closes.
29
29
  */
30
- export declare function CommandPalette({ open, commands, onClose, label, placeholder, showAll }: {
30
+ export declare function CommandPalette({ open, commands, onClose, label, placeholder, showAll, groupLabel }: {
31
31
  readonly open: boolean;
32
32
  readonly commands: readonly Command[];
33
33
  readonly onClose: () => void;
34
34
  readonly label?: string | undefined;
35
35
  readonly placeholder?: string | undefined;
36
36
  readonly showAll?: readonly string[] | undefined;
37
+ /** Presentation only: translate headings without changing group or showAll IDs. */
38
+ readonly groupLabel?: ((group: string) => string) | undefined;
37
39
  }): JSX.Element | null;