sh3-core 0.19.3 → 0.19.6

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 (43) hide show
  1. package/dist/api.d.ts +5 -0
  2. package/dist/api.js +3 -0
  3. package/dist/chrome/CompactChrome.svelte +34 -1
  4. package/dist/chrome/CompactChrome.svelte.test.js +4 -2
  5. package/dist/chrome/FloatsSheet.svelte +236 -0
  6. package/dist/chrome/FloatsSheet.svelte.d.ts +7 -0
  7. package/dist/chrome/FloatsSheet.svelte.test.d.ts +1 -0
  8. package/dist/chrome/FloatsSheet.svelte.test.js +155 -0
  9. package/dist/documents/picker-api.d.ts +31 -0
  10. package/dist/documents/picker-api.js +1 -0
  11. package/dist/documents/picker-api.test.d.ts +1 -0
  12. package/dist/documents/picker-api.test.js +132 -0
  13. package/dist/documents/picker-primitive.d.ts +7 -0
  14. package/dist/documents/picker-primitive.js +58 -0
  15. package/dist/layout/compact/CompactRenderer.svelte +8 -2
  16. package/dist/layout/compact/rootStore.svelte.d.ts +20 -0
  17. package/dist/layout/compact/rootStore.svelte.js +59 -0
  18. package/dist/layout/compact/rootStore.svelte.test.d.ts +1 -0
  19. package/dist/layout/compact/rootStore.svelte.test.js +54 -0
  20. package/dist/layout/floats.d.ts +27 -0
  21. package/dist/layout/floats.js +20 -0
  22. package/dist/layout/floats.test.js +34 -1
  23. package/dist/layout/inspection.js +25 -2
  24. package/dist/layout/inspection.svelte.test.js +49 -0
  25. package/dist/overlays/FloatLayer.svelte +12 -1
  26. package/dist/overlays/float.d.ts +7 -0
  27. package/dist/overlays/float.js +76 -6
  28. package/dist/overlays/float.test.js +170 -0
  29. package/dist/primitives/widgets/DocumentFilePicker.d.ts +25 -0
  30. package/dist/primitives/widgets/DocumentFilePicker.js +74 -0
  31. package/dist/primitives/widgets/DocumentFilePicker.svelte +144 -0
  32. package/dist/primitives/widgets/DocumentFilePicker.svelte.d.ts +18 -0
  33. package/dist/primitives/widgets/DocumentOpener.svelte +36 -0
  34. package/dist/primitives/widgets/DocumentOpener.svelte.d.ts +17 -0
  35. package/dist/primitives/widgets/DocumentSaver.svelte +36 -0
  36. package/dist/primitives/widgets/DocumentSaver.svelte.d.ts +17 -0
  37. package/dist/primitives/widgets/_DocumentBrowser.svelte +339 -0
  38. package/dist/primitives/widgets/_DocumentBrowser.svelte.d.ts +12 -0
  39. package/dist/shards/activate.svelte.js +23 -14
  40. package/dist/shards/types.d.ts +8 -0
  41. package/dist/version.d.ts +1 -1
  42. package/dist/version.js +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,17 @@
1
+ import type { CommitOnlyEvents } from './_contract';
2
+ import type { DocumentMeta } from '../../documents/types';
3
+ import type { SaverValue } from './DocumentFilePicker';
4
+ type DocListFn = () => Promise<Array<DocumentMeta & {
5
+ shardId: string;
6
+ }>>;
7
+ type $$ComponentProps = {
8
+ value?: SaverValue;
9
+ listDocuments: DocListFn;
10
+ disabled?: boolean;
11
+ invalid?: boolean;
12
+ size?: 'sm' | 'md';
13
+ buttonLabel?: string;
14
+ } & CommitOnlyEvents<SaverValue>;
15
+ declare const DocumentSaver: import("svelte").Component<$$ComponentProps, {}, "value">;
16
+ type DocumentSaver = ReturnType<typeof DocumentSaver>;
17
+ export default DocumentSaver;
@@ -0,0 +1,339 @@
1
+ <script lang="ts">
2
+ import {
3
+ buildTree,
4
+ formatSize,
5
+ formatDate,
6
+ iconForFile,
7
+ breadcrumbSegments,
8
+ type DocEntry,
9
+ type OpenerValue,
10
+ type SaverValue,
11
+ type FileItem,
12
+ } from './DocumentFilePicker';
13
+
14
+ let {
15
+ mode,
16
+ docs,
17
+ onCommit,
18
+ onCancel,
19
+ close,
20
+ suggestedName = '',
21
+ }: {
22
+ mode: 'open' | 'save';
23
+ docs: DocEntry[];
24
+ onCommit: (value: OpenerValue | SaverValue) => void;
25
+ onCancel: () => void;
26
+ close: () => void;
27
+ suggestedName?: string;
28
+ } = $props();
29
+
30
+ let shardId = $state<string | null>(null);
31
+ let prefix = $state('');
32
+ let selectedFile = $state<DocEntry | null>(null);
33
+ let filename = $state(suggestedName);
34
+ let activeIdx = $state(0);
35
+ let listEl = $state<HTMLElement | undefined>(undefined);
36
+
37
+ const items = $derived(buildTree(docs, shardId, prefix));
38
+ const crumbs = $derived(breadcrumbSegments(shardId, prefix));
39
+
40
+ $effect(() => {
41
+ items;
42
+ activeIdx = Math.min(activeIdx, items.length - 1);
43
+ });
44
+
45
+ function navigateShard(id: string) {
46
+ shardId = id;
47
+ prefix = '';
48
+ selectedFile = null;
49
+ filename = '';
50
+ activeIdx = 0;
51
+ }
52
+
53
+ function navigatePrefix(p: string) {
54
+ if (shardId === null) {
55
+ navigateShard(p);
56
+ } else {
57
+ prefix = p;
58
+ selectedFile = null;
59
+ filename = '';
60
+ activeIdx = 0;
61
+ }
62
+ }
63
+
64
+ function selectFile(item: FileItem) {
65
+ if (item.kind === 'folder') {
66
+ navigatePrefix(item.fullPath);
67
+ } else {
68
+ if (mode === 'open') {
69
+ selectedFile = item.doc;
70
+ }
71
+ if (mode === 'save') {
72
+ filename = item.name;
73
+ }
74
+ }
75
+ }
76
+
77
+ function commit() {
78
+ if (mode === 'open' && selectedFile) {
79
+ onCommit({ shardId: selectedFile.shardId, path: selectedFile.path });
80
+ close();
81
+ } else if (mode === 'save' && filename.trim() && shardId) {
82
+ const p = prefix ? `${shardId}/${prefix}/${filename}` : `${shardId}/${filename}`;
83
+ onCommit(p);
84
+ close();
85
+ }
86
+ }
87
+
88
+ function cancel() {
89
+ onCancel();
90
+ close();
91
+ }
92
+
93
+ function canCommit(): boolean {
94
+ if (mode === 'open') return selectedFile !== null;
95
+ return filename.trim().length > 0 && shardId !== null;
96
+ }
97
+
98
+ function onKey(e: KeyboardEvent) {
99
+ if (e.target instanceof HTMLInputElement) return;
100
+ switch (e.key) {
101
+ case 'ArrowDown':
102
+ e.preventDefault();
103
+ activeIdx = Math.min(activeIdx + 1, items.length - 1);
104
+ break;
105
+ case 'ArrowUp':
106
+ e.preventDefault();
107
+ activeIdx = Math.max(activeIdx - 1, 0);
108
+ break;
109
+ case 'Enter':
110
+ e.preventDefault();
111
+ if (activeIdx >= 0 && activeIdx < items.length) {
112
+ selectFile(items[activeIdx]);
113
+ }
114
+ break;
115
+ case 'Escape':
116
+ e.preventDefault();
117
+ cancel();
118
+ break;
119
+ case 'Backspace':
120
+ if (prefix) {
121
+ e.preventDefault();
122
+ const parts = prefix.split('/');
123
+ parts.pop();
124
+ prefix = parts.join('/');
125
+ activeIdx = 0;
126
+ } else if (shardId) {
127
+ e.preventDefault();
128
+ shardId = null;
129
+ activeIdx = 0;
130
+ }
131
+ break;
132
+ }
133
+ }
134
+
135
+ function onDblClick(item: FileItem) {
136
+ if (item.kind === 'folder') {
137
+ navigatePrefix(item.fullPath);
138
+ } else if (mode === 'open') {
139
+ onCommit({ shardId: item.doc.shardId, path: item.doc.path });
140
+ close();
141
+ }
142
+ }
143
+ </script>
144
+
145
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
146
+ <div class="sh3-doc-browser" onkeydown={onKey} tabindex="-1">
147
+ <div class="sh3-doc-browser__head">
148
+ <span class="sh3-doc-browser__title">
149
+ {mode === 'open' ? 'Open Document' : 'Save Document'}
150
+ </span>
151
+ </div>
152
+
153
+ <nav class="sh3-doc-browser__crumbs">
154
+ {#each crumbs as seg, i}
155
+ {#if i > 0}<span class="sh3-doc-browser__crumb-sep">/</span>{/if}
156
+ <button
157
+ type="button"
158
+ class="sh3-doc-browser__crumb"
159
+ class:sh3-doc-browser__crumb--last={i === crumbs.length - 1}
160
+ onclick={() => {
161
+ shardId = seg.targetShard;
162
+ prefix = seg.targetPrefix;
163
+ selectedFile = null;
164
+ filename = '';
165
+ activeIdx = 0;
166
+ }}
167
+ >{seg.label}</button>
168
+ {/each}
169
+ </nav>
170
+
171
+ <div class="sh3-doc-browser__list" bind:this={listEl}>
172
+ {#if items.length === 0}
173
+ <div class="sh3-doc-browser__empty">
174
+ {shardId === null ? 'No shards available.' : prefix ? 'Empty directory.' : 'No documents in this shard.'}
175
+ </div>
176
+ {:else}
177
+ {#each items as item, i}
178
+ {@const isActive = i === activeIdx}
179
+ {@const isSelected = item.kind === 'file' && mode === 'open' && selectedFile?.path === item.doc.path && selectedFile?.shardId === item.doc.shardId}
180
+ <!-- svelte-ignore a11y_no_static_element_interactions -->
181
+ <div
182
+ class="sh3-doc-browser__item"
183
+ class:sh3-doc-browser__item--active={isActive}
184
+ class:sh3-doc-browser__item--selected={isSelected}
185
+ class:sh3-doc-browser__item--folder={item.kind === 'folder'}
186
+ role="option"
187
+ tabindex="-1"
188
+ aria-selected={isSelected}
189
+ onclick={() => selectFile(item)}
190
+ onkeydown={(e) => { if (e.key === 'Enter') selectFile(item); }}
191
+ ondblclick={() => onDblClick(item)}
192
+ onmouseenter={() => activeIdx = i}
193
+ >
194
+ {#if item.kind === 'folder'}
195
+ <span class="sh3-doc-browser__icon sh3-doc-browser__icon--folder" aria-hidden="true">📁</span>
196
+ <span class="sh3-doc-browser__name">{item.name}</span>
197
+ <span class="sh3-doc-browser__meta"></span>
198
+ {:else}
199
+ <span class="sh3-doc-browser__icon" aria-hidden="true">{iconForFile(item.name)}</span>
200
+ <span class="sh3-doc-browser__name">{item.name}</span>
201
+ <span class="sh3-doc-browser__meta">
202
+ {formatSize(item.doc.size)}
203
+ <span class="sh3-doc-browser__date">{formatDate(item.doc.lastModified)}</span>
204
+ </span>
205
+ {/if}
206
+ </div>
207
+ {/each}
208
+ {/if}
209
+ </div>
210
+
211
+ {#if mode === 'save' && shardId !== null}
212
+ <div class="sh3-doc-browser__save-row">
213
+ <span class="sh3-doc-browser__save-label">Filename:</span>
214
+ <input
215
+ class="sh3-doc-browser__save-input"
216
+ type="text"
217
+ placeholder="filename.ext"
218
+ bind:value={filename}
219
+ onkeydown={(e) => { if (e.key === 'Enter' && canCommit()) commit(); }}
220
+ />
221
+ </div>
222
+ {/if}
223
+
224
+ <div class="sh3-doc-browser__footer">
225
+ <button type="button" class="sh3-doc-browser__btn sh3-doc-browser__btn--cancel" onclick={cancel}>Cancel</button>
226
+ <button
227
+ type="button"
228
+ class="sh3-doc-browser__btn sh3-doc-browser__btn--primary"
229
+ disabled={!canCommit()}
230
+ onclick={commit}
231
+ >
232
+ {mode === 'open' ? 'Open' : 'Save'}
233
+ </button>
234
+ </div>
235
+ </div>
236
+
237
+ <style>
238
+ .sh3-doc-browser {
239
+ background: var(--sh3-bg-elevated);
240
+ border: 1px solid var(--sh3-border-strong);
241
+ border-radius: var(--sh3-widget-radius);
242
+ box-shadow: var(--sh3-shadow-lg);
243
+ width: 420px;
244
+ max-height: 480px;
245
+ display: flex; flex-direction: column;
246
+ overflow: hidden;
247
+ color: var(--sh3-fg);
248
+ font-size: 0.8125rem;
249
+ outline: none;
250
+ }
251
+ .sh3-doc-browser__head {
252
+ display: flex; align-items: center; justify-content: space-between;
253
+ padding: 8px 12px;
254
+ border-bottom: 1px solid var(--sh3-border);
255
+ }
256
+ .sh3-doc-browser__title { font-weight: 600; }
257
+ .sh3-doc-browser__crumbs {
258
+ display: flex; align-items: center; gap: 2px;
259
+ padding: 4px 8px;
260
+ border-bottom: 1px solid var(--sh3-border);
261
+ overflow-x: auto;
262
+ flex-shrink: 0;
263
+ }
264
+ .sh3-doc-browser__crumb {
265
+ display: inline-flex; align-items: center;
266
+ padding: 1px 4px;
267
+ border: none; background: none;
268
+ color: var(--sh3-fg-muted);
269
+ font: inherit; font-size: 0.75rem;
270
+ cursor: pointer;
271
+ border-radius: var(--sh3-radius-sm);
272
+ white-space: nowrap;
273
+ flex-shrink: 0;
274
+ }
275
+ .sh3-doc-browser__crumb:hover { background: var(--sh3-bg); color: var(--sh3-fg); }
276
+ .sh3-doc-browser__crumb--last { color: var(--sh3-fg); font-weight: 600; }
277
+ .sh3-doc-browser__crumb-sep { color: var(--sh3-fg-subtle); font-size: 0.75rem; flex-shrink: 0; }
278
+ .sh3-doc-browser__list { flex: 1; overflow-y: auto; padding: 4px 0; min-height: 0; }
279
+ .sh3-doc-browser__item { display: flex; align-items: center; gap: 8px; padding: 5px 12px; cursor: pointer; }
280
+ .sh3-doc-browser__item--active { background: var(--sh3-bg); }
281
+ .sh3-doc-browser__item--selected { background: var(--sh3-accent); color: var(--sh3-fg-on-accent); }
282
+ .sh3-doc-browser__item--active.sh3-doc-browser__item--selected { background: var(--sh3-accent); }
283
+ .sh3-doc-browser__icon { flex-shrink: 0; width: 18px; text-align: center; font-size: 0.875rem; }
284
+ .sh3-doc-browser__icon--folder { font-size: 0.75rem; }
285
+ .sh3-doc-browser__name {
286
+ flex: 1; min-width: 0;
287
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
288
+ font-family: var(--sh3-font-mono); font-size: 0.75rem;
289
+ }
290
+ .sh3-doc-browser__item--folder .sh3-doc-browser__name { font-family: inherit; font-size: 0.8125rem; }
291
+ .sh3-doc-browser__meta {
292
+ display: flex; align-items: center; gap: 10px;
293
+ flex-shrink: 0; font-size: 0.6875rem; color: var(--sh3-fg-muted);
294
+ }
295
+ .sh3-doc-browser__item--selected .sh3-doc-browser__meta { color: var(--sh3-fg-on-accent); opacity: 0.8; }
296
+ .sh3-doc-browser__date { color: var(--sh3-fg-subtle); }
297
+ .sh3-doc-browser__item--selected .sh3-doc-browser__date { color: var(--sh3-fg-on-accent); opacity: 0.65; }
298
+ .sh3-doc-browser__empty { padding: 20px 12px; text-align: center; color: var(--sh3-fg-muted); font-style: italic; }
299
+ .sh3-doc-browser__save-row {
300
+ display: flex; align-items: center; gap: 8px;
301
+ padding: 6px 12px; border-top: 1px solid var(--sh3-border);
302
+ }
303
+ .sh3-doc-browser__save-label { font-size: 0.75rem; color: var(--sh3-fg-muted); flex-shrink: 0; }
304
+ .sh3-doc-browser__save-input {
305
+ flex: 1;
306
+ height: var(--sh3-field-height-sm);
307
+ padding: 0 8px;
308
+ background: var(--sh3-input-bg);
309
+ border: 1px solid var(--sh3-border);
310
+ border-radius: var(--sh3-radius-sm);
311
+ color: var(--sh3-fg);
312
+ font: inherit; font-family: var(--sh3-font-mono); font-size: 0.75rem;
313
+ outline: none;
314
+ }
315
+ .sh3-doc-browser__save-input:focus { border-color: var(--sh3-input-border-focus); box-shadow: var(--sh3-focus-ring); }
316
+ .sh3-doc-browser__footer {
317
+ display: flex; justify-content: flex-end; gap: 8px;
318
+ padding: 8px 12px; border-top: 1px solid var(--sh3-border);
319
+ }
320
+ .sh3-doc-browser__btn {
321
+ display: inline-flex; align-items: center;
322
+ height: 26px; padding: 0 12px;
323
+ border: 1px solid var(--sh3-border);
324
+ border-radius: var(--sh3-radius-sm);
325
+ background: var(--sh3-bg-elevated);
326
+ color: var(--sh3-fg);
327
+ font: inherit; font-size: 0.75rem;
328
+ cursor: pointer;
329
+ }
330
+ .sh3-doc-browser__btn:hover { background: var(--sh3-bg); }
331
+ .sh3-doc-browser__btn--primary {
332
+ background: var(--sh3-accent);
333
+ color: var(--sh3-fg-on-accent);
334
+ border-color: var(--sh3-accent);
335
+ }
336
+ .sh3-doc-browser__btn--primary:hover { filter: brightness(1.1); }
337
+ .sh3-doc-browser__btn--primary:disabled { opacity: 0.5; cursor: not-allowed; }
338
+ .sh3-doc-browser__btn--primary:disabled:hover { filter: none; }
339
+ </style>
@@ -0,0 +1,12 @@
1
+ import { type DocEntry, type OpenerValue, type SaverValue } from './DocumentFilePicker';
2
+ type $$ComponentProps = {
3
+ mode: 'open' | 'save';
4
+ docs: DocEntry[];
5
+ onCommit: (value: OpenerValue | SaverValue) => void;
6
+ onCancel: () => void;
7
+ close: () => void;
8
+ suggestedName?: string;
9
+ };
10
+ declare const DocumentBrowser: import("svelte").Component<$$ComponentProps, {}, "">;
11
+ type DocumentBrowser = ReturnType<typeof DocumentBrowser>;
12
+ export default DocumentBrowser;
@@ -28,6 +28,7 @@ import { createZoneManager } from '../state/manage';
28
28
  import { PERMISSION_STATE_MANAGE } from '../state/types';
29
29
  import { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from '../documents/types';
30
30
  import { createBrowseCapability } from '../documents/browse';
31
+ import { createDocumentPicker } from '../documents/picker-primitive';
31
32
  import { createShardKeysApi } from '../keys/client';
32
33
  import { PERMISSION_KEYS_MINT } from '../keys/types';
33
34
  import { subscribe } from '../keys/revocation-bus.svelte';
@@ -88,7 +89,7 @@ export function registerShard(shard) {
88
89
  * @throws If the shard is not registered, if `shard.activate` throws, or if a manifest view has no factory after activation.
89
90
  */
90
91
  export async function activateShard(id, opts) {
91
- var _a, _b, _c, _d, _e, _f, _g;
92
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l;
92
93
  const shard = registeredShards.get(id);
93
94
  if (!shard) {
94
95
  throw new Error(`Cannot activate shard "${id}": not registered`);
@@ -143,6 +144,13 @@ export async function activateShard(id, opts) {
143
144
  return off;
144
145
  },
145
146
  };
147
+ const hasBrowse = (_a = shard.manifest.permissions) === null || _a === void 0 ? void 0 : _a.includes(PERMISSION_DOCUMENTS_BROWSE);
148
+ const browseCap = hasBrowse
149
+ ? createBrowseCapability(getTenantId(), getDocumentBackend(), {
150
+ canRead: (_c = (_b = shard.manifest.permissions) === null || _b === void 0 ? void 0 : _b.includes(PERMISSION_DOCUMENTS_READ)) !== null && _c !== void 0 ? _c : false,
151
+ canWrite: (_e = (_d = shard.manifest.permissions) === null || _d === void 0 ? void 0 : _d.includes(PERMISSION_DOCUMENTS_WRITE)) !== null && _e !== void 0 ? _e : false,
152
+ })
153
+ : undefined;
146
154
  const ctx = {
147
155
  state: (schema) => sh3.state(id, schema),
148
156
  registerView: (viewId, factory) => {
@@ -212,19 +220,20 @@ export async function activateShard(id, opts) {
212
220
  get tenantId() {
213
221
  return getTenantId();
214
222
  },
215
- zones: ((_a = shard.manifest.permissions) === null || _a === void 0 ? void 0 : _a.includes(PERMISSION_STATE_MANAGE))
223
+ zones: ((_f = shard.manifest.permissions) === null || _f === void 0 ? void 0 : _f.includes(PERMISSION_STATE_MANAGE))
216
224
  ? createZoneManager()
217
225
  : undefined,
218
- browse: ((_b = shard.manifest.permissions) === null || _b === void 0 ? void 0 : _b.includes(PERMISSION_DOCUMENTS_BROWSE))
219
- ? createBrowseCapability(getTenantId(), getDocumentBackend(), {
220
- canRead: shard.manifest.permissions.includes(PERMISSION_DOCUMENTS_READ),
221
- canWrite: shard.manifest.permissions.includes(PERMISSION_DOCUMENTS_WRITE),
222
- })
223
- : undefined,
224
- keys: ((_c = shard.manifest.permissions) === null || _c === void 0 ? void 0 : _c.includes(PERMISSION_KEYS_MINT))
226
+ browse: browseCap,
227
+ documentPicker: browseCap
228
+ ? createDocumentPicker(() => browseCap.listDocuments())
229
+ : createDocumentPicker(async () => {
230
+ const docs = await getDocumentBackend().list(getTenantId(), id);
231
+ return docs.map(d => (Object.assign(Object.assign({}, d), { shardId: id })));
232
+ }),
233
+ keys: ((_g = shard.manifest.permissions) === null || _g === void 0 ? void 0 : _g.includes(PERMISSION_KEYS_MINT))
225
234
  ? createShardKeysApi({
226
235
  shardId: id,
227
- shardPermissions: (_d = shard.manifest.permissions) !== null && _d !== void 0 ? _d : [],
236
+ shardPermissions: (_h = shard.manifest.permissions) !== null && _h !== void 0 ? _h : [],
228
237
  })
229
238
  : undefined,
230
239
  contributions,
@@ -241,7 +250,7 @@ export async function activateShard(id, opts) {
241
250
  sh3: makeSh3Api({
242
251
  callerKind: 'shard',
243
252
  callerShardId: id,
244
- zones: ((_e = shard.manifest.permissions) === null || _e === void 0 ? void 0 : _e.includes(PERMISSION_STATE_MANAGE))
253
+ zones: ((_j = shard.manifest.permissions) === null || _j === void 0 ? void 0 : _j.includes(PERMISSION_STATE_MANAGE))
245
254
  ? createZoneManager()
246
255
  : undefined,
247
256
  }),
@@ -290,7 +299,7 @@ export async function activateShard(id, opts) {
290
299
  try {
291
300
  void fn();
292
301
  }
293
- catch (_h) {
302
+ catch (_m) {
294
303
  // intentionally swallowed: original error is what matters.
295
304
  }
296
305
  }
@@ -304,7 +313,7 @@ export async function activateShard(id, opts) {
304
313
  erroredShards.set(id, {
305
314
  id,
306
315
  error: err,
307
- phase: (_f = opts === null || opts === void 0 ? void 0 : opts.phase) !== null && _f !== void 0 ? _f : 'launch',
316
+ phase: (_k = opts === null || opts === void 0 ? void 0 : opts.phase) !== null && _k !== void 0 ? _k : 'launch',
308
317
  timestamp: Date.now(),
309
318
  });
310
319
  console.error(`[sh3] Shard "${id}" failed to activate:`, err);
@@ -312,7 +321,7 @@ export async function activateShard(id, opts) {
312
321
  }
313
322
  // Activation succeeded — clear any prior error record for this shard.
314
323
  erroredShards.delete(id);
315
- void ((_g = shard.autostart) === null || _g === void 0 ? void 0 : _g.call(shard, ctx));
324
+ void ((_l = shard.autostart) === null || _l === void 0 ? void 0 : _l.call(shard, ctx));
316
325
  }
317
326
  /**
318
327
  * Deactivate an active shard. Calls `shard.deactivate`, flushes and disposes
@@ -2,6 +2,7 @@ import type { StateZones } from '../state/zones.svelte';
2
2
  import type { ZoneSchema, ZoneManager } from '../state/types';
3
3
  import type { DocumentHandle, DocumentHandleOptions } from '../documents/types';
4
4
  import type { BrowseCapability } from '../documents/browse';
5
+ import type { DocumentPickerApi } from '../documents/picker-api';
5
6
  import type { EnvState } from '../env/types';
6
7
  import type { Verb } from '../verbs/types';
7
8
  import type { Sh3Api } from '../verbs/types';
@@ -287,6 +288,13 @@ export interface ShardContext {
287
288
  * owning shard's own `ctx.documents()` handle.
288
289
  */
289
290
  browse?: BrowseCapability;
291
+ /**
292
+ * Programmatic document picker. Opens the DocumentBrowser popup scoped
293
+ * to this shard's document zone (or wider tree if the shard has the
294
+ * `documents:browse` permission). Returns the selected document reference
295
+ * or null if the user cancelled.
296
+ */
297
+ documentPicker: DocumentPickerApi;
290
298
  /**
291
299
  * Mint/list/revoke keys minted by this shard. Only available when the
292
300
  * manifest declares the `keys:mint` permission.
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export declare const VERSION = "0.19.3";
2
+ export declare const VERSION = "0.19.6";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** Auto-generated from package.json — do not edit manually. */
2
- export const VERSION = '0.19.3';
2
+ export const VERSION = '0.19.6';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.19.3",
3
+ "version": "0.19.6",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"