sh3-core 0.10.2 → 0.10.5

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 (57) hide show
  1. package/dist/__test__/fixtures.js +1 -0
  2. package/dist/api.d.ts +2 -0
  3. package/dist/api.js +1 -0
  4. package/dist/conflicts/ConflictModal.svelte +131 -0
  5. package/dist/conflicts/ConflictModal.svelte.d.ts +19 -0
  6. package/dist/conflicts/DetailView.svelte +198 -0
  7. package/dist/conflicts/DetailView.svelte.d.ts +17 -0
  8. package/dist/conflicts/PromptView.svelte +55 -0
  9. package/dist/conflicts/PromptView.svelte.d.ts +9 -0
  10. package/dist/conflicts/adapter-documents.d.ts +3 -0
  11. package/dist/conflicts/adapter-documents.js +119 -0
  12. package/dist/conflicts/api.d.ts +108 -0
  13. package/dist/conflicts/api.js +33 -0
  14. package/dist/conflicts/most-recent.d.ts +3 -0
  15. package/dist/conflicts/most-recent.js +23 -0
  16. package/dist/conflicts/most-recent.test.d.ts +1 -0
  17. package/dist/conflicts/most-recent.test.js +45 -0
  18. package/dist/conflicts/renderer-registry.d.ts +7 -0
  19. package/dist/conflicts/renderer-registry.js +59 -0
  20. package/dist/conflicts/renderer-registry.test.d.ts +1 -0
  21. package/dist/conflicts/renderer-registry.test.js +124 -0
  22. package/dist/conflicts/renderers/MetaOnlyRenderer.svelte +73 -0
  23. package/dist/conflicts/renderers/MetaOnlyRenderer.svelte.d.ts +9 -0
  24. package/dist/conflicts/renderers/TextDiffRenderer.svelte +154 -0
  25. package/dist/conflicts/renderers/TextDiffRenderer.svelte.d.ts +9 -0
  26. package/dist/conflicts/renderers/index.d.ts +8 -0
  27. package/dist/conflicts/renderers/index.js +63 -0
  28. package/dist/conflicts/resolve-primitive.d.ts +2 -0
  29. package/dist/conflicts/resolve-primitive.js +55 -0
  30. package/dist/conflicts/shell-api.d.ts +2 -0
  31. package/dist/conflicts/shell-api.js +13 -0
  32. package/dist/documents/browse.d.ts +26 -0
  33. package/dist/documents/browse.js +16 -0
  34. package/dist/documents/browse.test.js +97 -0
  35. package/dist/documents/handle.js +5 -0
  36. package/dist/documents/handle.test.js +37 -0
  37. package/dist/documents/http-backend.d.ts +1 -0
  38. package/dist/documents/http-backend.js +9 -0
  39. package/dist/documents/http-backend.test.d.ts +1 -0
  40. package/dist/documents/http-backend.test.js +39 -0
  41. package/dist/documents/types.d.ts +14 -0
  42. package/dist/keys/ConsentDialog.svelte +1 -0
  43. package/dist/layout/LayoutRenderer.browser.test.js +78 -0
  44. package/dist/layout/LayoutRenderer.svelte +1 -0
  45. package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-6-fixed-slots-freezes-the-handle-adjacent-to-a-fixed-pane--dblclick-does-not-collapse-1.png +0 -0
  46. package/dist/layout/__screenshots__/LayoutRenderer.browser.test.ts/LayoutRenderer-browser---E-6-fixed-slots-hides-the-collapse-widget-on-a-fixed-pane-1.png +0 -0
  47. package/dist/layout/types.d.ts +8 -0
  48. package/dist/primitives/ResizableSplitter.svelte +38 -3
  49. package/dist/primitives/ResizableSplitter.svelte.d.ts +7 -0
  50. package/dist/primitives/base.css +140 -1
  51. package/dist/shell-shard/InputLine.svelte +1 -0
  52. package/dist/shellRuntime.svelte.d.ts +3 -0
  53. package/dist/shellRuntime.svelte.js +2 -0
  54. package/dist/tokens.css +3 -1
  55. package/dist/version.d.ts +1 -1
  56. package/dist/version.js +1 -1
  57. package/package.json +1 -1
@@ -28,6 +28,7 @@
28
28
  sizes,
29
29
  pinned,
30
30
  collapsed,
31
+ fixed,
31
32
  count,
32
33
  pane,
33
34
  onResize,
@@ -46,6 +47,13 @@
46
47
  pinned?: SizeMode[];
47
48
  /** Per-pane collapsed state. Omitted entries default to false. */
48
49
  collapsed?: boolean[];
50
+ /**
51
+ * Per-pane fixed flag. A fixed pane has no collapse widget and
52
+ * the handles on either side of it are frozen (non-interactive,
53
+ * rendered thinner). A non-fixed pane whose every neighbor is
54
+ * fixed also loses its collapse widget.
55
+ */
56
+ fixed?: boolean[];
49
57
  /** Number of panes — `sizes.length` should match. */
50
58
  count: number;
51
59
  /** Snippet invoked once per pane with the pane index. */
@@ -67,6 +75,15 @@
67
75
 
68
76
  const modeOf = (i: number): SizeMode => pinned?.[i] ?? 'fr';
69
77
  const isCollapsed = (i: number): boolean => collapsed?.[i] ?? false;
78
+ const isFixed = (i: number): boolean => fixed?.[i] ?? false;
79
+ const isHandleFrozen = (i: number): boolean => isFixed(i) || isFixed(i + 1);
80
+
81
+ function canCollapse(i: number): boolean {
82
+ if (isFixed(i)) return false;
83
+ const left = i > 0 ? !isFixed(i - 1) : false;
84
+ const right = i < count - 1 ? !isFixed(i + 1) : false;
85
+ return left || right;
86
+ }
70
87
 
71
88
  /** CSS `flex` shorthand for pane i. */
72
89
  function flexFor(i: number): string {
@@ -88,8 +105,9 @@
88
105
  let drag: DragState | null = $state(null);
89
106
 
90
107
  function beginDrag(e: PointerEvent, handleIndex: number) {
91
- // Disable resize handles adjacent to collapsed panes.
108
+ // Disable resize handles adjacent to collapsed or fixed panes.
92
109
  if (isCollapsed(handleIndex) || isCollapsed(handleIndex + 1)) return;
110
+ if (isHandleFrozen(handleIndex)) return;
93
111
 
94
112
  e.preventDefault();
95
113
  (e.target as HTMLElement).setPointerCapture(e.pointerId);
@@ -198,12 +216,13 @@
198
216
  <span class="collapse-icon">{direction === 'horizontal' ? '▸' : '▾'}</span>
199
217
  </button>
200
218
  {:else}
201
- {#if onCollapseToggle}
219
+ {#if onCollapseToggle && canCollapse(i)}
202
220
  <button
203
221
  type="button"
204
222
  class="collapse-header expanded"
205
223
  class:horizontal={direction === 'horizontal'}
206
224
  class:vertical={direction === 'vertical'}
225
+ data-testid="collapse-toggle-{i}"
207
226
  onclick={() => onCollapseToggle?.(i, true)}
208
227
  aria-label="Collapse pane"
209
228
  >
@@ -221,12 +240,17 @@
221
240
  class="splitter-handle"
222
241
  class:dragging={drag?.handleIndex === i}
223
242
  class:disabled={isCollapsed(i) || isCollapsed(i + 1)}
243
+ class:frozen={isHandleFrozen(i)}
224
244
  data-testid="splitter-handle-{i}"
225
245
  onpointerdown={(e) => beginDrag(e, i)}
226
246
  onpointermove={moveDrag}
227
247
  onpointerup={endDrag}
228
248
  onpointercancel={endDrag}
229
- ondblclick={() => onCollapseToggle?.(i, !isCollapsed(i))}
249
+ ondblclick={() => {
250
+ if (isHandleFrozen(i)) return;
251
+ if (!canCollapse(i) && !isCollapsed(i)) return;
252
+ onCollapseToggle?.(i, !isCollapsed(i));
253
+ }}
230
254
  role="separator"
231
255
  aria-orientation={direction === 'horizontal' ? 'vertical' : 'horizontal'}
232
256
  ></div>
@@ -323,6 +347,15 @@
323
347
  cursor: default;
324
348
  pointer-events: none;
325
349
  }
350
+ .splitter-handle.frozen {
351
+ cursor: default;
352
+ pointer-events: none;
353
+ background: var(--shell-border);
354
+ opacity: 0.5;
355
+ }
356
+ .splitter-handle.frozen:hover {
357
+ background: var(--shell-border);
358
+ }
326
359
 
327
360
  .horizontal > .splitter-handle {
328
361
  width: 4px;
@@ -332,4 +365,6 @@
332
365
  height: 4px;
333
366
  cursor: row-resize;
334
367
  }
368
+ .horizontal > .splitter-handle.frozen { width: 1px; }
369
+ .vertical > .splitter-handle.frozen { height: 1px; }
335
370
  </style>
@@ -14,6 +14,13 @@ type $$ComponentProps = {
14
14
  pinned?: SizeMode[];
15
15
  /** Per-pane collapsed state. Omitted entries default to false. */
16
16
  collapsed?: boolean[];
17
+ /**
18
+ * Per-pane fixed flag. A fixed pane has no collapse widget and
19
+ * the handles on either side of it are frozen (non-interactive,
20
+ * rendered thinner). A non-fixed pane whose every neighbor is
21
+ * fixed also loses its collapse widget.
22
+ */
23
+ fixed?: boolean[];
17
24
  /** Number of panes — `sizes.length` should match. */
18
25
  count: number;
19
26
  /** Snippet invoked once per pane with the pane index. */
@@ -16,7 +16,7 @@ input[type="reset"],
16
16
  .shell-base-button {
17
17
  padding: 6px 14px;
18
18
  background: var(--shell-accent, #6ea8fe);
19
- color: #fff;
19
+ color: var(--shell-fg, #fff);
20
20
  border: none;
21
21
  border-radius: var(--shell-radius);
22
22
  cursor: pointer;
@@ -40,3 +40,142 @@ input[type="reset"]:active,
40
40
  .shell-base-button:active {
41
41
  filter: brightness(0.92);
42
42
  }
43
+
44
+ /* ── Text inputs ─────────────────────────────────────────────────────── */
45
+
46
+ input[type="text"],
47
+ input[type="email"],
48
+ input[type="password"],
49
+ input[type="search"],
50
+ input[type="url"],
51
+ input[type="tel"],
52
+ input[type="number"],
53
+ textarea,
54
+ .shell-base-input {
55
+ padding: var(--shell-pad-md) var(--shell-pad-lg);
56
+ background: var(--shell-input-bg);
57
+ color: var(--shell-fg);
58
+ border: 1px solid var(--shell-border);
59
+ border-radius: var(--shell-radius);
60
+ font-family: inherit;
61
+ font-size: 0.8125rem;
62
+ line-height: var(--shell-line);
63
+ }
64
+
65
+ ::placeholder { color: var(--shell-fg-muted); }
66
+
67
+ input:focus-visible,
68
+ textarea:focus-visible,
69
+ .shell-base-input:focus-visible {
70
+ border-color: var(--shell-input-border-focus);
71
+ box-shadow: var(--shell-focus-ring);
72
+ outline: none;
73
+ }
74
+
75
+ input:disabled,
76
+ textarea:disabled,
77
+ .shell-base-input[aria-disabled="true"] {
78
+ opacity: 0.55;
79
+ cursor: not-allowed;
80
+ }
81
+
82
+ textarea {
83
+ resize: vertical;
84
+ min-height: calc(var(--shell-line) * 3em);
85
+ }
86
+
87
+ /* ── Checkbox & radio ────────────────────────────────────────────────── */
88
+
89
+ input[type="checkbox"].shell-base-check,
90
+ input[type="radio"].shell-base-radio {
91
+ appearance: none;
92
+ width: 14px;
93
+ height: 14px;
94
+ margin: 0;
95
+ background: var(--shell-input-bg);
96
+ border: 1px solid var(--shell-border);
97
+ cursor: pointer;
98
+ display: inline-grid;
99
+ place-content: center;
100
+ flex-shrink: 0;
101
+ }
102
+
103
+ .shell-base-check { border-radius: var(--shell-radius-sm); }
104
+ .shell-base-radio { border-radius: 50%; }
105
+
106
+ .shell-base-check:checked,
107
+ .shell-base-radio:checked {
108
+ background: var(--shell-accent);
109
+ border-color: var(--shell-accent);
110
+ }
111
+
112
+ .shell-base-check:checked::before {
113
+ content: "";
114
+ width: 8px;
115
+ height: 8px;
116
+ background: #fff;
117
+ clip-path: polygon(14% 44%, 0 60%, 40% 100%, 100% 20%, 85% 8%, 38% 70%);
118
+ }
119
+
120
+ .shell-base-radio:checked::before {
121
+ content: "";
122
+ width: 6px;
123
+ height: 6px;
124
+ border-radius: 50%;
125
+ background: #fff;
126
+ }
127
+
128
+ .shell-base-check:focus-visible,
129
+ .shell-base-radio:focus-visible {
130
+ box-shadow: var(--shell-focus-ring);
131
+ outline: none;
132
+ }
133
+
134
+ .shell-base-check:disabled,
135
+ .shell-base-radio:disabled {
136
+ opacity: 0.55;
137
+ cursor: not-allowed;
138
+ }
139
+
140
+ /* ── Switch ──────────────────────────────────────────────────────────── */
141
+
142
+ input[type="checkbox"].shell-base-switch {
143
+ appearance: none;
144
+ position: relative;
145
+ width: 28px;
146
+ height: 16px;
147
+ margin: 0;
148
+ background: var(--shell-border-strong);
149
+ border-radius: 999px;
150
+ cursor: pointer;
151
+ transition: background 120ms ease;
152
+ flex-shrink: 0;
153
+ }
154
+
155
+ .shell-base-switch::before {
156
+ content: "";
157
+ position: absolute;
158
+ top: 2px;
159
+ left: 2px;
160
+ width: 12px;
161
+ height: 12px;
162
+ background: var(--shell-fg);
163
+ border-radius: 50%;
164
+ transition: transform 120ms ease;
165
+ }
166
+
167
+ .shell-base-switch:checked { background: var(--shell-accent); }
168
+ .shell-base-switch:checked::before {
169
+ transform: translateX(12px);
170
+ background: #fff;
171
+ }
172
+
173
+ .shell-base-switch:focus-visible {
174
+ box-shadow: var(--shell-focus-ring);
175
+ outline: none;
176
+ }
177
+
178
+ .shell-base-switch:disabled {
179
+ opacity: 0.55;
180
+ cursor: not-allowed;
181
+ }
@@ -119,6 +119,7 @@
119
119
  .shell-input-cwd { color: var(--shell-fg-muted, #888); }
120
120
  .shell-input-arrow { color: var(--shell-accent, #6cf); }
121
121
  .shell-input-field {
122
+ padding: 0;
122
123
  flex: 1 1 auto;
123
124
  background: transparent;
124
125
  border: 0;
@@ -5,6 +5,7 @@ import { type PopupManager } from './overlays/popup';
5
5
  import { type ToastManager } from './overlays/toast';
6
6
  import { type FloatManager } from './overlays/float';
7
7
  import { type PresetManager } from './overlays/presets';
8
+ import type { ConflictsApi } from './conflicts/api';
8
9
  /**
9
10
  * The process-wide shell singleton exposed to shards and the shell's own
10
11
  * internal code. Provides state zone creation and overlay managers.
@@ -28,6 +29,8 @@ export interface Shell {
28
29
  float: FloatManager;
29
30
  /** Named layout presets per app. See overlays/presets.ts. */
30
31
  presets: PresetManager;
32
+ /** Conflict manager view. Shell-owned modal for conflict arbitration. */
33
+ conflicts: ConflictsApi;
31
34
  }
32
35
  /** The process-wide shell instance. Framework-internal code uses this directly; shards receive a scoped view via `ShardContext`. */
33
36
  export declare const shell: Shell;
@@ -20,6 +20,7 @@ import { popupManager } from './overlays/popup';
20
20
  import { toastManager } from './overlays/toast';
21
21
  import { floatManager } from './overlays/float';
22
22
  import { presetManager } from './overlays/presets';
23
+ import { conflictsApi } from './conflicts/shell-api';
23
24
  /** The process-wide shell instance. Framework-internal code uses this directly; shards receive a scoped view via `ShardContext`. */
24
25
  export const shell = {
25
26
  state: createStateZones,
@@ -28,4 +29,5 @@ export const shell = {
28
29
  toast: toastManager,
29
30
  float: floatManager,
30
31
  presets: presetManager,
32
+ conflicts: conflictsApi,
31
33
  };
package/dist/tokens.css CHANGED
@@ -33,7 +33,9 @@
33
33
  --shell-accent-muted: #3a5580;
34
34
 
35
35
  /* Inputs */
36
- --shell-input-bg: #2a2a2a;
36
+ --shell-input-bg: #2a2a2a;
37
+ --shell-input-border-focus: var(--shell-accent);
38
+ --shell-focus-ring: 0 0 0 2px color-mix(in srgb, var(--shell-accent) 40%, transparent);
37
39
 
38
40
  /* Semantic */
39
41
  --shell-error: #f87171;
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.10.2";
2
+ export declare const VERSION = "0.10.5";
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.10.2';
2
+ export const VERSION = '0.10.5';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sh3-core",
3
- "version": "0.10.2",
3
+ "version": "0.10.5",
4
4
  "type": "module",
5
5
  "files": [
6
6
  "dist"