sh3-core 0.10.2 → 0.10.4

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 (49) hide show
  1. package/dist/api.d.ts +2 -0
  2. package/dist/api.js +1 -0
  3. package/dist/conflicts/ConflictModal.svelte +131 -0
  4. package/dist/conflicts/ConflictModal.svelte.d.ts +19 -0
  5. package/dist/conflicts/DetailView.svelte +198 -0
  6. package/dist/conflicts/DetailView.svelte.d.ts +17 -0
  7. package/dist/conflicts/PromptView.svelte +55 -0
  8. package/dist/conflicts/PromptView.svelte.d.ts +9 -0
  9. package/dist/conflicts/adapter-documents.d.ts +3 -0
  10. package/dist/conflicts/adapter-documents.js +119 -0
  11. package/dist/conflicts/api.d.ts +108 -0
  12. package/dist/conflicts/api.js +33 -0
  13. package/dist/conflicts/most-recent.d.ts +3 -0
  14. package/dist/conflicts/most-recent.js +23 -0
  15. package/dist/conflicts/most-recent.test.d.ts +1 -0
  16. package/dist/conflicts/most-recent.test.js +45 -0
  17. package/dist/conflicts/renderer-registry.d.ts +7 -0
  18. package/dist/conflicts/renderer-registry.js +59 -0
  19. package/dist/conflicts/renderer-registry.test.d.ts +1 -0
  20. package/dist/conflicts/renderer-registry.test.js +124 -0
  21. package/dist/conflicts/renderers/MetaOnlyRenderer.svelte +73 -0
  22. package/dist/conflicts/renderers/MetaOnlyRenderer.svelte.d.ts +9 -0
  23. package/dist/conflicts/renderers/TextDiffRenderer.svelte +154 -0
  24. package/dist/conflicts/renderers/TextDiffRenderer.svelte.d.ts +9 -0
  25. package/dist/conflicts/renderers/index.d.ts +8 -0
  26. package/dist/conflicts/renderers/index.js +63 -0
  27. package/dist/conflicts/resolve-primitive.d.ts +2 -0
  28. package/dist/conflicts/resolve-primitive.js +55 -0
  29. package/dist/conflicts/shell-api.d.ts +2 -0
  30. package/dist/conflicts/shell-api.js +13 -0
  31. package/dist/documents/browse.d.ts +26 -0
  32. package/dist/documents/browse.js +16 -0
  33. package/dist/documents/browse.test.js +97 -0
  34. package/dist/documents/handle.js +5 -0
  35. package/dist/documents/handle.test.js +37 -0
  36. package/dist/documents/http-backend.d.ts +1 -0
  37. package/dist/documents/http-backend.js +9 -0
  38. package/dist/documents/http-backend.test.d.ts +1 -0
  39. package/dist/documents/http-backend.test.js +39 -0
  40. package/dist/documents/types.d.ts +14 -0
  41. package/dist/keys/ConsentDialog.svelte +1 -0
  42. package/dist/primitives/base.css +140 -1
  43. package/dist/shell-shard/InputLine.svelte +1 -0
  44. package/dist/shellRuntime.svelte.d.ts +3 -0
  45. package/dist/shellRuntime.svelte.js +2 -0
  46. package/dist/tokens.css +3 -1
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +1 -1
package/dist/api.d.ts CHANGED
@@ -22,6 +22,8 @@ export type { BrowseCapability } from './documents/browse';
22
22
  export type { ContributionsApi } from './contributions/types';
23
23
  export type { SyncPolicy, SyncPolicyRule, DocStatus, ConflictFile, ConflictBranch, } from './documents/sync-types';
24
24
  export { PERMISSION_SYNC_PEER, PERMISSION_SYNC_POLICY } from './documents/sync-types';
25
+ export type { ConflictItem, ConflictBranch as ConflictManagerBranch, ResolveOptions as ConflictResolveOptions, ResolveOutcome as ConflictResolveOutcome, ResolveDocumentsInput as ConflictResolveDocumentsInput, DocsResolveOutcome as ConflictDocsResolveOutcome, ConflictRenderer, ConflictRendererProps, ConflictsApi, } from './conflicts/api';
26
+ export { CONFLICT_RENDERER_POINT, ConflictPermissionError, ConflictSessionOrphanedError, } from './conflicts/api';
25
27
  export { registeredShards, activeShards } from './shards/activate.svelte';
26
28
  export type { RegistryIndex, PackageEntry, PackageVersion, RequiredDependency, InstalledPackage, InstallResult, PackageMeta, } from './registry/types';
27
29
  export type { ResolvedPackage } from './registry/client';
package/dist/api.js CHANGED
@@ -31,6 +31,7 @@ export { launchApp, returnToHome, unregisterApp } from './apps/lifecycle';
31
31
  export { inspectActiveLayout, spliceIntoActiveLayout, dockIntoActiveLayout, focusTab, focusView, collapseChild, expandChild, closeTab, } from './layout/inspection';
32
32
  export { PERMISSION_DOCUMENTS_BROWSE, PERMISSION_DOCUMENTS_READ, PERMISSION_DOCUMENTS_WRITE, } from './documents/types';
33
33
  export { PERMISSION_SYNC_PEER, PERMISSION_SYNC_POLICY } from './documents/sync-types';
34
+ export { CONFLICT_RENDERER_POINT, ConflictPermissionError, ConflictSessionOrphanedError, } from './conflicts/api';
34
35
  // Shard introspection — read-only reactive maps exposing which shards are
35
36
  // known to the host and which are currently active. Intended for diagnostic
36
37
  // and tooling shards that need to visualize framework state. Phase 9
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { onDestroy } from 'svelte';
3
+ import type { ConflictItem, ConflictRenderer, ConflictBranch } from './api';
4
+ import PromptView from './PromptView.svelte';
5
+ import DetailView from './DetailView.svelte';
6
+ import { pickMostRecent } from './most-recent';
7
+
8
+ interface Props {
9
+ items: ConflictItem[];
10
+ title: string;
11
+ mostRecentBy: (a: ConflictBranch, b: ConflictBranch) => number;
12
+ requirePrompt: boolean;
13
+ contributed: ConflictRenderer[];
14
+ onResolve: (
15
+ choices: Array<{ itemId: string; chosen: ConflictBranch; meta?: Record<string, unknown> }>,
16
+ skipped: string[],
17
+ ) => void;
18
+ onCancel: () => void;
19
+ /** Provided by shell.modal.open wrapping; we must call to actually close. */
20
+ close: () => void;
21
+ }
22
+ const props: Props = $props();
23
+
24
+ type Phase = 'prompt' | 'detail';
25
+ // Initial values — items / requirePrompt don't change mid-session.
26
+ // svelte-ignore state_referenced_locally
27
+ const startsInPrompt = props.items.length >= 2 || props.requirePrompt;
28
+ let phase = $state<Phase>(startsInPrompt ? 'prompt' : 'detail');
29
+
30
+ // Initial selections: most-recent per item. Items don't change mid-session,
31
+ // so using the initial value here is the intended semantics.
32
+ // svelte-ignore state_referenced_locally
33
+ let selections = $state<Record<string, string>>(
34
+ Object.fromEntries(
35
+ props.items.map((it) => {
36
+ const pick = pickMostRecent(it.branches, props.mostRecentBy)?.origin
37
+ ?? it.branches[0]?.origin
38
+ ?? '';
39
+ return [it.id, pick];
40
+ }),
41
+ ),
42
+ );
43
+ let skipped = $state<Record<string, boolean>>({});
44
+ let currentIndex = $state(0);
45
+
46
+ // Settlement guard — the safety net in onDestroy relies on this to
47
+ // distinguish normal completion from unexpected dismissal (Escape,
48
+ // programmatic closeAll, caller-shard deactivation).
49
+ let settled = false;
50
+
51
+ onDestroy(() => {
52
+ if (!settled) {
53
+ settled = true;
54
+ props.onCancel();
55
+ }
56
+ });
57
+
58
+ function finalize(): void {
59
+ if (settled) return;
60
+ settled = true;
61
+ const choices: Array<{ itemId: string; chosen: ConflictBranch; meta?: Record<string, unknown> }> = [];
62
+ const skippedIds: string[] = [];
63
+ for (const it of props.items) {
64
+ if (skipped[it.id]) {
65
+ skippedIds.push(it.id);
66
+ continue;
67
+ }
68
+ const origin = selections[it.id];
69
+ const branch = it.branches.find((b) => b.origin === origin);
70
+ if (branch) choices.push({ itemId: it.id, chosen: branch, meta: it.meta });
71
+ }
72
+ props.onResolve(choices, skippedIds);
73
+ props.close();
74
+ }
75
+
76
+ function cancel(): void {
77
+ if (settled) return;
78
+ settled = true;
79
+ props.onCancel();
80
+ props.close();
81
+ }
82
+
83
+ function overwriteMostRecent(): void {
84
+ // Selections are already pre-filled to most-recent; finalize directly.
85
+ finalize();
86
+ }
87
+
88
+ function resolveIndividually(): void {
89
+ phase = 'detail';
90
+ }
91
+
92
+ function select(itemId: string, origin: string): void {
93
+ selections = { ...selections, [itemId]: origin };
94
+ if (skipped[itemId]) {
95
+ const nextSkipped = { ...skipped };
96
+ delete nextSkipped[itemId];
97
+ skipped = nextSkipped;
98
+ }
99
+ }
100
+
101
+ function toggleSkip(itemId: string): void {
102
+ skipped = { ...skipped, [itemId]: !skipped[itemId] };
103
+ }
104
+
105
+ function navigate(nextIndex: number): void {
106
+ currentIndex = nextIndex;
107
+ }
108
+ </script>
109
+
110
+ {#if phase === 'prompt'}
111
+ <PromptView
112
+ count={props.items.length}
113
+ onOverwriteMostRecent={overwriteMostRecent}
114
+ onResolveIndividually={resolveIndividually}
115
+ onCancel={cancel}
116
+ />
117
+ {:else}
118
+ <DetailView
119
+ items={props.items}
120
+ {selections}
121
+ {skipped}
122
+ {currentIndex}
123
+ title={props.title}
124
+ contributed={props.contributed}
125
+ onSelect={select}
126
+ onNavigate={navigate}
127
+ onToggleSkip={toggleSkip}
128
+ onDone={finalize}
129
+ onCancel={cancel}
130
+ />
131
+ {/if}
@@ -0,0 +1,19 @@
1
+ import type { ConflictItem, ConflictRenderer, ConflictBranch } from './api';
2
+ interface Props {
3
+ items: ConflictItem[];
4
+ title: string;
5
+ mostRecentBy: (a: ConflictBranch, b: ConflictBranch) => number;
6
+ requirePrompt: boolean;
7
+ contributed: ConflictRenderer[];
8
+ onResolve: (choices: Array<{
9
+ itemId: string;
10
+ chosen: ConflictBranch;
11
+ meta?: Record<string, unknown>;
12
+ }>, skipped: string[]) => void;
13
+ onCancel: () => void;
14
+ /** Provided by shell.modal.open wrapping; we must call to actually close. */
15
+ close: () => void;
16
+ }
17
+ declare const ConflictModal: import("svelte").Component<Props, {}, "">;
18
+ type ConflictModal = ReturnType<typeof ConflictModal>;
19
+ export default ConflictModal;
@@ -0,0 +1,198 @@
1
+ <script lang="ts">
2
+ import type { ConflictItem, ConflictRenderer, ConflictRendererProps } from './api';
3
+ import { pickRenderer } from './renderer-registry';
4
+
5
+ interface Props {
6
+ items: ConflictItem[];
7
+ selections: Record<string, string>; // itemId → chosen origin
8
+ skipped: Record<string, boolean>; // itemId → true when skipped
9
+ currentIndex: number;
10
+ title: string;
11
+ contributed: ConflictRenderer[];
12
+ onSelect: (itemId: string, origin: string) => void;
13
+ onNavigate: (nextIndex: number) => void;
14
+ onToggleSkip: (itemId: string) => void;
15
+ onDone: () => void;
16
+ onCancel: () => void;
17
+ }
18
+ const props: Props = $props();
19
+
20
+ const current = $derived(props.items[props.currentIndex]);
21
+ const selectedOrigin = $derived(
22
+ current
23
+ ? (props.selections[current.id] ?? current.branches[0]?.origin ?? '')
24
+ : '',
25
+ );
26
+ const renderer = $derived(current ? pickRenderer(current, props.contributed) : null);
27
+
28
+ let rendererContainer: HTMLDivElement | undefined = $state();
29
+ let currentUnmount: (() => void) | null = null;
30
+
31
+ $effect(() => {
32
+ // Depend on current + renderer + selectedOrigin so the effect re-runs
33
+ // whenever any of them changes; parent-owned selection drives re-mount.
34
+ void current;
35
+ void renderer;
36
+ void selectedOrigin;
37
+ if (currentUnmount) { currentUnmount(); currentUnmount = null; }
38
+ if (!rendererContainer || !current || !renderer) return;
39
+ const rendererProps: ConflictRendererProps = {
40
+ item: current,
41
+ selectedOrigin,
42
+ onSelect: (origin) => props.onSelect(current.id, origin),
43
+ };
44
+ currentUnmount = renderer.mount(rendererContainer, rendererProps);
45
+ return () => {
46
+ if (currentUnmount) { currentUnmount(); currentUnmount = null; }
47
+ };
48
+ });
49
+
50
+ function prev(): void {
51
+ if (props.currentIndex > 0) props.onNavigate(props.currentIndex - 1);
52
+ }
53
+ function next(): void {
54
+ if (props.currentIndex < props.items.length - 1) props.onNavigate(props.currentIndex + 1);
55
+ }
56
+
57
+ const itemStates = $derived(
58
+ props.items.map((it) => {
59
+ if (props.skipped[it.id]) return 'skipped' as const;
60
+ if (props.selections[it.id]) return 'resolved' as const;
61
+ return 'pending' as const;
62
+ }),
63
+ );
64
+ </script>
65
+
66
+ <div class="sh3-conflict-detail">
67
+ <header class="sh3-conflict-detail-head">
68
+ <h3>{props.title}</h3>
69
+ {#if props.items.length > 1}
70
+ <span class="sh3-conflict-detail-counter">
71
+ {props.currentIndex + 1} / {props.items.length}
72
+ </span>
73
+ {/if}
74
+ </header>
75
+
76
+ <div class="sh3-conflict-detail-body">
77
+ {#if props.items.length > 1}
78
+ <aside class="sh3-conflict-detail-rail">
79
+ {#each props.items as it, i (it.id)}
80
+ <button
81
+ type="button"
82
+ class="sh3-conflict-detail-rail-item"
83
+ class:selected={i === props.currentIndex}
84
+ onclick={() => props.onNavigate(i)}
85
+ >
86
+ <span class="sh3-conflict-detail-rail-state {itemStates[i]}">
87
+ {#if itemStates[i] === 'resolved'}✓{:else if itemStates[i] === 'skipped'}⟶{:else}·{/if}
88
+ </span>
89
+ <span class="sh3-conflict-detail-rail-label">{it.label}</span>
90
+ </button>
91
+ {/each}
92
+ </aside>
93
+ {/if}
94
+
95
+ <section class="sh3-conflict-detail-main">
96
+ {#if current}
97
+ <div class="sh3-conflict-detail-branches">
98
+ {#each current.branches as b (b.origin)}
99
+ <label class="sh3-conflict-detail-branch">
100
+ <input
101
+ type="radio"
102
+ name="branch-{current.id}"
103
+ value={b.origin}
104
+ checked={selectedOrigin === b.origin}
105
+ onchange={() => props.onSelect(current.id, b.origin)}
106
+ />
107
+ <span>{b.origin} · v{b.version}</span>
108
+ </label>
109
+ {/each}
110
+ </div>
111
+ <div bind:this={rendererContainer} class="sh3-conflict-detail-renderer"></div>
112
+ {/if}
113
+ </section>
114
+ </div>
115
+
116
+ <footer class="sh3-conflict-detail-foot">
117
+ <div class="sh3-conflict-detail-nav">
118
+ <button type="button" onclick={prev} disabled={props.currentIndex === 0}>← Prev</button>
119
+ <button type="button" onclick={() => current && props.onToggleSkip(current.id)}>
120
+ {current && props.skipped[current.id] ? 'Unskip' : 'Skip'}
121
+ </button>
122
+ <button type="button" onclick={next} disabled={props.currentIndex >= props.items.length - 1}>
123
+ Next →
124
+ </button>
125
+ </div>
126
+ <div class="sh3-conflict-detail-actions">
127
+ <button type="button" class="sh3-conflict-detail-cancel" onclick={props.onCancel}>Cancel</button>
128
+ <button type="button" class="sh3-conflict-detail-done" onclick={props.onDone}>Done</button>
129
+ </div>
130
+ </footer>
131
+ </div>
132
+
133
+ <style>
134
+ .sh3-conflict-detail {
135
+ display: flex; flex-direction: column;
136
+ width: min(920px, 96vw); height: min(640px, 90vh);
137
+ background: var(--shell-bg, #1e1e1e);
138
+ color: var(--shell-fg, #e0e0e0);
139
+ border: 1px solid var(--shell-border, #444);
140
+ border-radius: var(--shell-radius-md);
141
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
142
+ }
143
+ .sh3-conflict-detail-head {
144
+ padding: 12px 16px; border-bottom: 1px solid var(--shell-border, #444);
145
+ display: flex; justify-content: space-between; align-items: center;
146
+ }
147
+ .sh3-conflict-detail-head h3 { margin: 0; font-size: 1rem; font-weight: 600; }
148
+ .sh3-conflict-detail-counter { color: var(--shell-fg-muted, #888); font-size: 0.8125rem; }
149
+ .sh3-conflict-detail-body { display: flex; flex: 1; min-height: 0; }
150
+ .sh3-conflict-detail-rail {
151
+ width: 240px; border-right: 1px solid var(--shell-border, #444);
152
+ overflow-y: auto; display: flex; flex-direction: column;
153
+ }
154
+ .sh3-conflict-detail-rail-item {
155
+ display: flex; gap: 8px; align-items: center;
156
+ padding: 6px 12px; text-align: left;
157
+ background: transparent; border: none; color: inherit;
158
+ cursor: pointer; font: inherit; font-size: 0.8125rem;
159
+ }
160
+ .sh3-conflict-detail-rail-item:hover { background: var(--shell-input-bg, #2a2a2a); }
161
+ .sh3-conflict-detail-rail-item.selected { background: var(--shell-input-bg, #2a2a2a); font-weight: 600; }
162
+ .sh3-conflict-detail-rail-state { width: 1em; text-align: center; opacity: 0.7; }
163
+ .sh3-conflict-detail-rail-state.resolved { color: var(--shell-success, #3a3); opacity: 1; }
164
+ .sh3-conflict-detail-rail-state.skipped { color: var(--shell-fg-muted, #888); }
165
+ .sh3-conflict-detail-rail-label {
166
+ flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
167
+ }
168
+ .sh3-conflict-detail-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
169
+ .sh3-conflict-detail-branches {
170
+ display: flex; gap: 12px; padding: 8px 16px;
171
+ border-bottom: 1px solid var(--shell-border, #444);
172
+ flex-wrap: wrap;
173
+ }
174
+ .sh3-conflict-detail-branch { display: flex; gap: 6px; align-items: center; font-size: 0.8125rem; }
175
+ .sh3-conflict-detail-renderer { flex: 1; min-height: 0; overflow: auto; }
176
+ .sh3-conflict-detail-foot {
177
+ padding: 10px 16px; border-top: 1px solid var(--shell-border, #444);
178
+ display: flex; justify-content: space-between; gap: 8px;
179
+ }
180
+ .sh3-conflict-detail-nav, .sh3-conflict-detail-actions { display: flex; gap: 8px; }
181
+ .sh3-conflict-detail-nav button, .sh3-conflict-detail-actions button {
182
+ padding: 6px 14px;
183
+ background: var(--shell-input-bg, #2a2a2a);
184
+ color: var(--shell-fg, #e0e0e0);
185
+ border: 1px solid var(--shell-border, #444);
186
+ border-radius: var(--shell-radius);
187
+ font: inherit;
188
+ font-size: 0.8125rem;
189
+ cursor: pointer;
190
+ }
191
+ .sh3-conflict-detail-nav button:disabled { opacity: 0.5; cursor: not-allowed; }
192
+ .sh3-conflict-detail-done {
193
+ background: var(--shell-accent, #007acc) !important;
194
+ color: #fff !important;
195
+ font-weight: 600;
196
+ border-color: var(--shell-accent, #007acc) !important;
197
+ }
198
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { ConflictItem, ConflictRenderer } from './api';
2
+ interface Props {
3
+ items: ConflictItem[];
4
+ selections: Record<string, string>;
5
+ skipped: Record<string, boolean>;
6
+ currentIndex: number;
7
+ title: string;
8
+ contributed: ConflictRenderer[];
9
+ onSelect: (itemId: string, origin: string) => void;
10
+ onNavigate: (nextIndex: number) => void;
11
+ onToggleSkip: (itemId: string) => void;
12
+ onDone: () => void;
13
+ onCancel: () => void;
14
+ }
15
+ declare const DetailView: import("svelte").Component<Props, {}, "">;
16
+ type DetailView = ReturnType<typeof DetailView>;
17
+ export default DetailView;
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ interface Props {
3
+ count: number;
4
+ onOverwriteMostRecent: () => void;
5
+ onResolveIndividually: () => void;
6
+ onCancel: () => void;
7
+ }
8
+ const { count, onOverwriteMostRecent, onResolveIndividually, onCancel }: Props = $props();
9
+ </script>
10
+
11
+ <div class="sh3-conflict-prompt">
12
+ <h3 class="sh3-conflict-prompt-title">
13
+ {count} conflict{count === 1 ? '' : 's'} to resolve
14
+ </h3>
15
+ <p class="sh3-conflict-prompt-intro">
16
+ Pick one option for the whole batch, or resolve each conflict individually.
17
+ </p>
18
+ <div class="sh3-conflict-prompt-actions">
19
+ <!-- svelte-ignore a11y_autofocus -->
20
+ <button type="button" class="sh3-conflict-prompt-primary" onclick={onOverwriteMostRecent} autofocus>
21
+ Overwrite with most recent
22
+ </button>
23
+ <button type="button" class="sh3-conflict-prompt-secondary" onclick={onResolveIndividually}>
24
+ Resolve individually
25
+ </button>
26
+ <button type="button" class="sh3-conflict-prompt-cancel" onclick={onCancel}>
27
+ Cancel
28
+ </button>
29
+ </div>
30
+ </div>
31
+
32
+ <style>
33
+ .sh3-conflict-prompt { padding: 20px; display: flex; flex-direction: column; gap: 14px; min-width: 360px; max-width: 480px; }
34
+ .sh3-conflict-prompt-title { margin: 0; font-size: 1.0625rem; font-weight: 600; color: var(--shell-fg, #e0e0e0); }
35
+ .sh3-conflict-prompt-intro { margin: 0; color: var(--shell-fg-muted, #888); font-size: 0.875rem; }
36
+ .sh3-conflict-prompt-actions { display: flex; flex-direction: column; gap: 8px; }
37
+ .sh3-conflict-prompt-actions button {
38
+ padding: 10px 14px;
39
+ border-radius: var(--shell-radius);
40
+ border: 1px solid var(--shell-border, #444);
41
+ background: var(--shell-input-bg, #2a2a2a);
42
+ color: var(--shell-fg, #e0e0e0);
43
+ font-size: 0.875rem;
44
+ font-family: inherit;
45
+ cursor: pointer;
46
+ text-align: left;
47
+ }
48
+ .sh3-conflict-prompt-primary {
49
+ background: var(--shell-accent, #007acc) !important;
50
+ border-color: var(--shell-accent, #007acc) !important;
51
+ color: #fff !important;
52
+ font-weight: 600;
53
+ }
54
+ .sh3-conflict-prompt-cancel { color: var(--shell-fg-muted, #888) !important; }
55
+ </style>
@@ -0,0 +1,9 @@
1
+ interface Props {
2
+ count: number;
3
+ onOverwriteMostRecent: () => void;
4
+ onResolveIndividually: () => void;
5
+ onCancel: () => void;
6
+ }
7
+ declare const PromptView: import("svelte").Component<Props, {}, "">;
8
+ type PromptView = ReturnType<typeof PromptView>;
9
+ export default PromptView;
@@ -0,0 +1,3 @@
1
+ import type { ShardContext } from '../shards/types';
2
+ import { type DocsResolveOutcome, type ResolveDocumentsInput, type ResolveOptions } from './api';
3
+ export declare function resolveDocuments(ctx: ShardContext, input: ResolveDocumentsInput, opts?: ResolveOptions): Promise<DocsResolveOutcome>;
@@ -0,0 +1,119 @@
1
+ /*
2
+ * Doc-zone adapter for shell.conflicts.
3
+ *
4
+ * Reads DocStatus for each requested path, filters to those actually in
5
+ * conflict, fetches per-branch content for preview, and invokes the
6
+ * generic primitive. On resolve, applies each chosen branch via the
7
+ * appropriate handle (own-shard via ctx.documents(); cross-shard via
8
+ * ctx.browse.resolveConflictFrom).
9
+ *
10
+ * Cross-shard requires the caller's manifest to declare documents:browse
11
+ * + documents:read + documents:write — detected via the presence of the
12
+ * capability methods on ctx.browse. The adapter throws
13
+ * ConflictPermissionError when any required capability is missing.
14
+ *
15
+ * Apply step is sequential; HTTP resolveConflict races the server's
16
+ * per-doc tick counter when parallelized. Failure isolation: one failed
17
+ * resolveConflict does not stop the batch; the caller gets both lists.
18
+ */
19
+ import { ConflictPermissionError, } from './api';
20
+ import { resolvePrimitive } from './resolve-primitive';
21
+ function ownShardHandle(ctx) {
22
+ // No extension filter — the adapter takes caller-supplied paths, any type.
23
+ const h = ctx.documents({ format: 'text' });
24
+ return {
25
+ status: (p) => h.status(p),
26
+ readBranch: (p, o) => h.readBranch(p, o),
27
+ resolveConflict: (p, c) => h.resolveConflict(p, c),
28
+ };
29
+ }
30
+ function crossShardHandle(ctx, shardId) {
31
+ const b = ctx.browse;
32
+ if (!(b === null || b === void 0 ? void 0 : b.statusFrom) || !(b === null || b === void 0 ? void 0 : b.readBranchFrom) || !(b === null || b === void 0 ? void 0 : b.resolveConflictFrom)) {
33
+ throw new ConflictPermissionError(`Cross-shard resolveDocuments requires documents:browse + documents:read + documents:write (shardId=${shardId})`);
34
+ }
35
+ return {
36
+ status: (p) => b.statusFrom(shardId, p),
37
+ readBranch: (p, o) => b.readBranchFrom(shardId, p, o),
38
+ resolveConflict: (p, c) => b.resolveConflictFrom(shardId, p, c),
39
+ };
40
+ }
41
+ function extractExtension(path) {
42
+ const i = path.lastIndexOf('.');
43
+ if (i < 0)
44
+ return undefined;
45
+ const j = path.lastIndexOf('/');
46
+ if (i < j)
47
+ return undefined; // dot is in a parent directory
48
+ return path.slice(i);
49
+ }
50
+ export async function resolveDocuments(ctx, input, opts = {}) {
51
+ var _a, _b;
52
+ const callerShardId = ctx.shardId;
53
+ const handle = input.shardId === callerShardId
54
+ ? ownShardHandle(ctx)
55
+ : crossShardHandle(ctx, input.shardId);
56
+ // Pre-filter: keep only paths actually in conflict state.
57
+ const statuses = [];
58
+ for (const path of input.paths) {
59
+ const s = await handle.status(path);
60
+ if (s && s.syncState === 'conflict' && s.branches && s.branches.length > 0) {
61
+ statuses.push({ path, status: s });
62
+ }
63
+ }
64
+ if (statuses.length === 0) {
65
+ return { status: 'resolved', resolved: [], failed: [], skipped: [] };
66
+ }
67
+ // Build ConflictItem[] with per-branch content fetched via readBranch.
68
+ const items = [];
69
+ for (const { path, status } of statuses) {
70
+ const extension = extractExtension(path);
71
+ const branches = [];
72
+ for (const b of (_a = status.branches) !== null && _a !== void 0 ? _a : []) {
73
+ const content = await handle.readBranch(path, b.origin).catch(() => null);
74
+ branches.push({
75
+ origin: b.origin,
76
+ version: b.version,
77
+ at: b.at,
78
+ content: content !== null && content !== void 0 ? content : undefined,
79
+ sizeBytes: content ? new TextEncoder().encode(content).length : undefined,
80
+ });
81
+ }
82
+ items.push({
83
+ id: path,
84
+ label: path,
85
+ extension,
86
+ branches,
87
+ meta: { shardId: input.shardId, path, docVersion: status.version },
88
+ });
89
+ }
90
+ const outcome = await resolvePrimitive(items, opts);
91
+ if (outcome.status === 'cancelled')
92
+ return { status: 'cancelled' };
93
+ // Apply each chosen branch sequentially.
94
+ const resolvedList = [];
95
+ const failedList = [];
96
+ for (const choice of outcome.choices) {
97
+ try {
98
+ await handle.resolveConflict(choice.itemId, { origin: choice.chosen.origin });
99
+ const after = await handle.status(choice.itemId).catch(() => null);
100
+ resolvedList.push({
101
+ path: choice.itemId,
102
+ chosen: choice.chosen,
103
+ appliedVersion: (_b = after === null || after === void 0 ? void 0 : after.version) !== null && _b !== void 0 ? _b : -1,
104
+ });
105
+ }
106
+ catch (err) {
107
+ failedList.push({
108
+ path: choice.itemId,
109
+ error: err instanceof Error ? err.message : String(err),
110
+ });
111
+ }
112
+ }
113
+ return {
114
+ status: 'resolved',
115
+ resolved: resolvedList,
116
+ failed: failedList,
117
+ skipped: outcome.skipped,
118
+ };
119
+ }