@adea-ai/ui 0.64.0 → 0.65.1
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 +50 -0
- package/dist/NOTICE +59 -1
- package/dist/components/composites/update-dialog/update-dialog.js +2 -2
- package/dist/components/composites/update-dialog/update-dialog.js.map +1 -1
- package/dist/components/layout/split-layout/drop.d.ts +13 -0
- package/dist/components/layout/split-layout/drop.d.ts.map +1 -0
- package/dist/components/layout/split-layout/drop.js +50 -0
- package/dist/components/layout/split-layout/drop.js.map +1 -0
- package/dist/components/layout/split-layout/geometry.d.ts +14 -0
- package/dist/components/layout/split-layout/geometry.d.ts.map +1 -0
- package/dist/components/layout/split-layout/geometry.js +44 -0
- package/dist/components/layout/split-layout/geometry.js.map +1 -0
- package/dist/components/layout/split-layout/index.d.ts +5 -0
- package/dist/components/layout/split-layout/index.d.ts.map +1 -0
- package/dist/components/layout/split-layout/index.js +4 -0
- package/dist/components/layout/split-layout/model.d.ts +51 -0
- package/dist/components/layout/split-layout/model.d.ts.map +1 -0
- package/dist/components/layout/split-layout/model.js +255 -0
- package/dist/components/layout/split-layout/model.js.map +1 -0
- package/dist/components/layout/split-layout/split-layout.d.ts +30 -0
- package/dist/components/layout/split-layout/split-layout.d.ts.map +1 -0
- package/dist/components/layout/split-layout/split-layout.js +312 -0
- package/dist/components/layout/split-layout/split-layout.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/r/registry.json +41 -0
- package/dist/r/split-layout.json +41 -0
- package/dist/r/src/components/composites/update-dialog/update-dialog.tsx +4 -4
- package/dist/r/src/components/layout/split-layout/drop.ts +29 -0
- package/dist/r/src/components/layout/split-layout/geometry.ts +40 -0
- package/dist/r/src/components/layout/split-layout/index.ts +4 -0
- package/dist/r/src/components/layout/split-layout/model.ts +336 -0
- package/dist/r/src/components/layout/split-layout/split-layout.tsx +346 -0
- package/package.json +10 -2
- package/registry.json +41 -0
- package/src/components/composites/update-dialog/update-dialog.tsx +4 -4
- package/src/components/layout/split-layout/drop.ts +29 -0
- package/src/components/layout/split-layout/geometry.ts +40 -0
- package/src/components/layout/split-layout/index.ts +4 -0
- package/src/components/layout/split-layout/model.ts +336 -0
- package/src/components/layout/split-layout/split-layout.tsx +346 -0
- package/src/index.ts +1 -0
package/README.md
CHANGED
|
@@ -218,6 +218,56 @@ acceptance and each application's mounted chat journey remain separate gates.
|
|
|
218
218
|
|
|
219
219
|
Apache-2.0.
|
|
220
220
|
|
|
221
|
+
## Binary layout model checkpoint
|
|
222
|
+
|
|
223
|
+
UI issue #22 extracts the existing attributed bb/Muxy Adea adaptation rather
|
|
224
|
+
than introducing another layout tree. `createLayoutState`, `splitPane`,
|
|
225
|
+
`movePane`, `closePane`, `undoClosePane`, `focusPane`, `swapPanes`,
|
|
226
|
+
`resizeSplit`, `normalizeLayout` and the reading-order helpers are pure.
|
|
227
|
+
Leaves extend `{kind: 'leaf', id: string}` with any host-owned payload. Closing
|
|
228
|
+
the last leaf calls an injected placeholder factory; it never selects a harness,
|
|
229
|
+
starts a terminal or deletes a project. Eight leaves/depth eight and 10–90%
|
|
230
|
+
ratios preserve the accepted contract. Construct or validate a tree before
|
|
231
|
+
editing it; the shared constructor validates visual structure and identities,
|
|
232
|
+
while applications still decode/scoped-persist preferences and retain unknown
|
|
233
|
+
future versions for recovery.
|
|
234
|
+
|
|
235
|
+
The model and maintained accessible renderer are implemented in this draft.
|
|
236
|
+
Stable content ownership and movement are exercised in component fixtures.
|
|
237
|
+
The packed renderer gate includes the UI #20 size-token correction and the host
|
|
238
|
+
presentation fixture. It checks the real package in compiled and Solid
|
|
239
|
+
conditions; complete shell composition, production adoption and release remain
|
|
240
|
+
required.
|
|
241
|
+
No persistent state, session services, native drag region or host framework is
|
|
242
|
+
imported into this model.
|
|
243
|
+
|
|
244
|
+
### Host pane presentation hooks
|
|
245
|
+
|
|
246
|
+
`SplitLayout` can keep its default pane header while allowing a host to supply
|
|
247
|
+
inline visible content with `renderPaneLabel`, opt pane regions into keyboard
|
|
248
|
+
Tab navigation with `paneTabIndex={0}`, and provide a domain-specific splitter
|
|
249
|
+
name with `labelForSeparator`. Pane regions remain programmatically focusable
|
|
250
|
+
by default (`paneTabIndex={-1}`), and separators keep their orientation-based
|
|
251
|
+
names unless the host supplies a label.
|
|
252
|
+
|
|
253
|
+
`renderPaneLabel` receives a stable `Accessor<L>` once for each leaf owner.
|
|
254
|
+
Read `leaf()` inside the returned JSX so Solid tracks payload changes while the
|
|
255
|
+
same pane owner is retained during split and move operations. This visible
|
|
256
|
+
header content does not define the region's accessible name or the close
|
|
257
|
+
button's name: both continue to come from `labelForLeaf`, so hosts should
|
|
258
|
+
provide an accessible domain label there as well.
|
|
259
|
+
|
|
260
|
+
The `HostPresentation` story shows a decorative icon and host title, keyboard
|
|
261
|
+
pane stops, and a workspace-specific separator name. A narrow-width component
|
|
262
|
+
fixture also checks that a long host title does not widen its pane header.
|
|
263
|
+
|
|
264
|
+
`bun run check:packed-layout` packs the real artifact, installs it in a disposable
|
|
265
|
+
consumer with peers omitted and scripts disabled, and exercises the direct model
|
|
266
|
+
subpath in native Node and the Solid source condition. It verifies zero runtime
|
|
267
|
+
imports and a 3 KiB gzip model budget (measured baseline: 2,455 bytes). This is
|
|
268
|
+
model-subpath evidence; it does not waive UI #16's package attribution/conditional
|
|
269
|
+
export corrections or the separate root compatibility gate.
|
|
270
|
+
|
|
221
271
|
### Controlled appearance editor
|
|
222
272
|
|
|
223
273
|
`AppearanceEditor` and `AppearancePopover` retain the accepted Zeron-derived
|
package/dist/NOTICE
CHANGED
|
@@ -49,6 +49,65 @@ Origin UI / coss (AGPL-3.0-or-later) was consulted for visual ideas only. No
|
|
|
49
49
|
code, class string, or asset from that project is reproduced here, because its
|
|
50
50
|
licence is incompatible with this distribution.
|
|
51
51
|
|
|
52
|
+
## Strict binary layout extracted from the accepted Adea donor port
|
|
53
|
+
|
|
54
|
+
`packages/ui/src/components/layout/split-layout/model.ts` is extracted from
|
|
55
|
+
Adea `packages/dev-view/src/layout/operations.ts` at
|
|
56
|
+
`0322ab09ff5e9775bfddc0bf2d81e1df436dbef7`, whose MIT portions substantially
|
|
57
|
+
translate bb `apps/app/src/lib/split-layout/ops.ts` at
|
|
58
|
+
`52a9256373d4d36f9b60e9e2a7f333464091a2ac` and Muxy
|
|
59
|
+
`Muxy/Models/Workspace/SplitNode.swift` at
|
|
60
|
+
`5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6`.
|
|
61
|
+
|
|
62
|
+
Copyright (c) 2026 Michael Yong
|
|
63
|
+
Copyright (c) 2026 Muxy
|
|
64
|
+
|
|
65
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
66
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
67
|
+
in the Software without restriction, including without limitation the rights
|
|
68
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
69
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
70
|
+
furnished to do so, subject to the following conditions:
|
|
71
|
+
|
|
72
|
+
The above copyright notice and this permission notice shall be included in all
|
|
73
|
+
copies or substantial portions of the Software.
|
|
74
|
+
|
|
75
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
76
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
77
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
78
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
79
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
80
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
81
|
+
SOFTWARE.
|
|
82
|
+
|
|
83
|
+
The accepted strict binary representation, reading-order insertion, parent
|
|
84
|
+
collapse, exact leaf identity, resize/normalize, move-at-cap and undo semantics
|
|
85
|
+
are retained. Opaque host leaves replace private domain types and the host
|
|
86
|
+
supplies the last-pane placeholder. Construction validates visual identities
|
|
87
|
+
and structural bounds; application scope/persistence, thread/runtime identity,
|
|
88
|
+
harness launch and process authority are excluded. Nearest test behavior from
|
|
89
|
+
Adea `packages/dev-view/tests/layout.test.ts` and bb `ops.test.ts` is preserved
|
|
90
|
+
where it applies to the accepted binary model. The accessible renderer and
|
|
91
|
+
application adoption remain separate gates under UI issue #22.
|
|
92
|
+
|
|
93
|
+
### Binary frame renderer continuation
|
|
94
|
+
|
|
95
|
+
`geometry.ts` translates `areaFrames` from the pinned Muxy source above. Its
|
|
96
|
+
fractional frames, horizontal/vertical offsets and exact node references are
|
|
97
|
+
retained; ratio repair uses the accepted 0.1–0.9 range rather than Muxy's 0–1.
|
|
98
|
+
`split-layout.tsx` adapts the accepted Adea pane-header/close/focus behavior
|
|
99
|
+
through generic callbacks, stable keyed leaf owners and Corvu separators.
|
|
100
|
+
It excludes application commands, persistence, harness/thread authority and
|
|
101
|
+
manual pointer listeners. This renderer is independently written against the
|
|
102
|
+
accepted model; it is not a port of Muxy's native UI or an additional layout tree.
|
|
103
|
+
The MIT attribution and permission above cover the translated frame algorithm.
|
|
104
|
+
|
|
105
|
+
The pane-drag continuation retains Adea's nearest-edge placement ordering and
|
|
106
|
+
pane-title gesture, replacing plaintext leaf IDs and string casts with typed
|
|
107
|
+
intent, an instance-local active drag payload, and cancellation/removal checks.
|
|
108
|
+
Header action composition injects application keyboard controls without owning
|
|
109
|
+
shortcuts or domain transitions. No new upstream source or license is introduced.
|
|
110
|
+
|
|
52
111
|
Busy composer action translated from KiroCrew
|
|
53
112
|
--------------------------------------------
|
|
54
113
|
Original: website/src/components/BusySendButton.tsx
|
|
@@ -71,7 +130,6 @@ action and unavailable reasons. No runtime queue or force-reset authority added.
|
|
|
71
130
|
Nearest tests: BusySendButton.menuKeyboard.test.tsx and
|
|
72
131
|
BusySendButton.slotScope.test.tsx; controlled mode replaces donor storage tests,
|
|
73
132
|
while keyboard/focus expectations are exercised in Chromium and WebKit.
|
|
74
|
-
|
|
75
133
|
## Plain transcript follow translated from KiroCrew
|
|
76
134
|
|
|
77
135
|
`packages/ui/src/components/conversation/scroll-follow-core.ts`,
|
|
@@ -14,8 +14,8 @@ var _tmpl$3 = /*#__PURE__*/ template(`<section class="grid gap-2"aria-label="Upd
|
|
|
14
14
|
var _tmpl$4 = /*#__PURE__*/ template(`<p class="flex items-center gap-2 text-sm text-success"role=status> is up to date.`);
|
|
15
15
|
var _tmpl$5 = /*#__PURE__*/ template(`<p class="flex items-center gap-2 text-sm text-success"role=status>`);
|
|
16
16
|
var _tmpl$6 = /*#__PURE__*/ template(`<p class="rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm text-destructive"role=alert>`);
|
|
17
|
-
var _tmpl$7 = /*#__PURE__*/ template(`<
|
|
18
|
-
var _tmpl$8 = /*#__PURE__*/ template(`<
|
|
17
|
+
var _tmpl$7 = /*#__PURE__*/ template(`<div class="flex flex-col gap-2"><div class="flex items-center gap-2"><h2 id=update-release-notes class="text-sm font-semibold">What changed in this release</h2></div><div class="max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground"role=region aria-labelledby=update-release-notes tabindex=0>`);
|
|
18
|
+
var _tmpl$8 = /*#__PURE__*/ template(`<div class="flex flex-col gap-2"><h2 id=update-changelog class="text-sm font-semibold">Installed changelog</h2><p class="text-xs text-muted-foreground">A plain-text history of the installed release channel.</p><div class="max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground"role=region aria-labelledby=update-changelog tabindex=0>`);
|
|
19
19
|
var _tmpl$9 = /*#__PURE__*/ template(`<div class="flex min-h-0 flex-col gap-5 overflow-y-auto p-6"><section class="rounded-xl border border-border bg-background/45 p-4"aria-label="Version status"><div class="flex flex-wrap items-start justify-between gap-4"><div class="flex flex-col gap-1"><p class="text-2xs font-semibold tracking-widest text-muted-foreground uppercase">Installed version</p><p class="text-xl font-semibold tracking-tight">v</p><p class="text-sm text-muted-foreground">`);
|
|
20
20
|
var _tmpl$0 = /*#__PURE__*/ template(`<p class="text-xs text-muted-foreground">Released `);
|
|
21
21
|
function errorMessage(caught, fallback) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-dialog.js","names":["createEffect","createMemo","createSignal","onMount","Show","splitProps","Check","Download","ExternalLink","LoaderCircle","RefreshCw","Sparkles","Badge","Button","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","Progress","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","isDesktopRuntime","UpdateDialogProps","adapter","appName","fallbackVersion","desktopOnlyMessage","open","defaultOpen","onOpenChange","class","errorMessage","caught","fallback","Error","message","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","undefined","desktop","setState","busy","setBusy","setError","loadStatus","active","current","checked","version","notes","value","working","triggerLabel","progressValue","Math","min","round","_$createComponent","children","when","as","variant","size","_$memo","_el$","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","_el$4","_tmpl$9","_el$5","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","type","disabled","onClick","_el$10","_tmpl$2","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$35","_tmpl$0","_el$36","_el$21","_tmpl$3","_el$22","aria-label","_el$23","_tmpl$4","_el$24","_el$25","_tmpl$5","_el$26","_tmpl$6","_el$27","_tmpl$7","_el$28","_el$29","_el$30","_el$31","_tmpl$8","_el$32","_el$33","_el$34","url","window","_$createComponent","_$memo"],"sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, onMount, Show, splitProps } from 'solid-js'\nimport { Check, Download, ExternalLink, LoaderCircle, RefreshCw, Sparkles } from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/button'\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../../ui/dialog'\nimport { Progress } from '../../ui/progress'\nimport { cn } from '#lib/utils'\nimport { formatBytes, formatReleaseDate, plainTextFromMarkdown } from '#lib/version-notes'\n\n/**\n * UpdateDialog.\n *\n * A software-update surface: the installed version, whether a newer release\n * exists, a download that shows its progress, and the release notes for what is\n * about to arrive.\n *\n * The transport is an adapter, because an update is the one thing a component\n * library genuinely cannot implement: it is signed, downloaded and applied by the\n * application's own updater, and the mechanism differs per shell. What *is*\n * general is the phase machine and the way each phase is presented — and that is\n * where the hard-won detail lives:\n *\n * **A refused install answers with a normal payload, not a rejection.** adea's\n * shell returns a status whose phase is `failed` when a download or an extract\n * fails, rather than throwing. Without handling that, clicking install looked like\n * nothing happened at all — the button spun, the phase never changed, and the\n * error sat unread in a field nobody rendered. The `failed` phase after an install\n * is therefore surfaced as an error, explicitly.\n *\n * **Busy is derived from the snapshot as well as from the click.** An update can\n * be downloading while the dialog is open — started before it opened, or by the\n * shell's own poll — so a local `busy` flag alone would offer an enabled Install\n * button mid-download.\n *\n * **Opening the dialog loads status first, then checks.** Status paints the\n * installed version immediately; the check is a network round trip. Doing them in\n * the other order leaves the dialog blank for as long as the check takes, on a\n * dialog whose first question is always \"what am I running?\".\n *\n * **Errors are not always `Error`s.** An adapter can reject with a string, and a\n * shell can answer with an error field. Both are read, and a fallback is shown\n * rather than an empty alert.\n *\n * The adapter's `isDesktopRuntime` gate exists because an update is a desktop\n * concern: a browser build has no installer, and the honest thing to show is that\n * updates are available in the desktop application rather than a disabled button\n * with no reason.\n */\n\n/** The phase an update is in. The union is the state machine. */\nexport type UpdatePhase =\n | 'idle'\n | 'checking'\n | 'current'\n | 'available'\n | 'downloading'\n | 'installing'\n | 'installed'\n | 'failed'\n\n/** A snapshot of the updater. Every field the dialog renders, and nothing else. */\nexport type UpdateState = Readonly<{\n phase: UpdatePhase\n /** The version currently installed. */\n currentVersion: string\n /** The version that can be installed, when one can. */\n availableVersion?: string | null\n /** A changelog for the installed channel, as markdown. */\n changelog?: string\n /** Notes for the available release, as markdown. */\n releaseNotes?: string | null\n /** The available release's date, as an ISO string. */\n releaseDate?: string | null\n /** Bytes downloaded so far, during `downloading`. */\n downloadedBytes?: number\n /** Total bytes, when the server sent a length. */\n totalBytes?: number | null\n /** Set when `phase` is `failed`, and sometimes when it is not. */\n error?: string | null\n /** A URL for the release's page. */\n releaseUrl?: string | null\n /** True when the application must restart to finish. */\n restartRequired?: boolean\n}>\n\n/**\n * The updater, as the application exposes it. Three calls, each answering with a\n * full snapshot rather than a delta — an updater's state is small, and a snapshot\n * cannot drift from the shell's own view of it.\n */\nexport type UpdateAdapter = Readonly<{\n /** The current state, without contacting the release channel. */\n getStatus(): Promise<UpdateState>\n /** Ask the release channel, and answer with what it found. */\n check(): Promise<UpdateState>\n /**\n * Download and apply `expectedVersion`.\n *\n * The argument is the version the caller believes is available, so the updater\n * can refuse a request that raced a newer release rather than installing the\n * wrong one.\n */\n install(expectedVersion: string): Promise<UpdateState>\n /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: string\n /** Shown before the first status arrives, so the dialog is never blank. */\n fallbackVersion?: string\n /** Copy for the adapter's non-desktop case. */\n desktopOnlyMessage?: string\n /** A controlled open state. Omit for the trigger to manage its own. */\n open?: boolean\n /**\n * Start open, uncontrolled — for a caller that opens the dialog because it found\n * something (\"an update is available\") rather than because the user asked. The\n * trigger is still rendered, so the dialog is reachable again after closing.\n */\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Render only the panel, for a caller that provides its own dialog. */\n class?: string\n}\n\nfunction errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error && caught.message) return caught.message\n if (typeof caught === 'string' && caught) return caught\n return fallback\n}\n\n/**\n * The phases during which the updater is working. `downloading` and `installing`\n * are included because the shell can be in either while the dialog is open, and\n * offering an enabled Install button during a download is how a double-install\n * happens.\n */\nfunction isBusy(state: UpdateState | null): boolean {\n return (\n state?.phase === 'checking' || state?.phase === 'downloading' || state?.phase === 'installing'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'fallbackVersion',\n 'desktopOnlyMessage',\n 'open',\n 'defaultOpen',\n 'onOpenChange',\n 'class',\n ])\n\n const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false)\n const open = () => local.open ?? uncontrolledOpen()\n const setOpen = (next: boolean) => {\n if (local.open === undefined) setUncontrolledOpen(next)\n local.onOpenChange?.(next)\n }\n\n const appName = () => local.appName ?? 'the application'\n const desktop = () => local.adapter.isDesktopRuntime()\n const [state, setState] = createSignal<UpdateState | null>(null)\n const [busy, setBusy] = createSignal(false)\n const [error, setError] = createSignal('')\n\n const loadStatus = async () => {\n if (!desktop()) return\n try {\n setState(await local.adapter.getStatus())\n } catch (caught) {\n setError(errorMessage(caught, 'Version status is unavailable'))\n }\n }\n\n onMount(() => {\n if (desktop()) void loadStatus()\n })\n\n const check = async () => {\n if (!desktop()) {\n setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`)\n return\n }\n setBusy(true)\n setError('')\n try {\n setState(await local.adapter.check())\n } catch (caught) {\n setError(errorMessage(caught, 'Could not check for updates'))\n await loadStatus()\n } finally {\n setBusy(false)\n }\n }\n\n /**\n * Status first, then the check — and an `active` flag, because the dialog can\n * close while the check is in flight and a late `setState` would write into a\n * disposed scope.\n */\n createEffect(() => {\n if (!open() || !desktop()) return\n let active = true\n setError('')\n setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!active) return\n setState(current)\n const checked = await local.adapter.check()\n if (active) setState(checked)\n } catch (caught) {\n if (active) setError(errorMessage(caught, 'Could not check for updates'))\n } finally {\n if (active) setBusy(false)\n }\n })()\n return () => {\n active = false\n }\n })\n\n const install = async () => {\n const version = state()?.availableVersion\n if (!version) return\n setBusy(true)\n setError('')\n try {\n const next = await local.adapter.install(version)\n setState(next)\n // A refused install answers with a normal payload whose phase is `failed`.\n // Without this the click looked like nothing happened.\n if (next.phase === 'failed') {\n setError(errorMessage(next.error, 'Update installation failed'))\n }\n } catch (caught) {\n setError(errorMessage(caught, 'Update installation failed'))\n await loadStatus()\n } finally {\n setBusy(false)\n }\n }\n\n const changelog = createMemo(() => plainTextFromMarkdown(state()?.changelog ?? ''))\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || isBusy(state())\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\n if (!current) return `${appName()} ${fallback}`\n switch (current.phase) {\n case 'checking':\n return 'Checking for updates…'\n case 'available':\n return current.availableVersion\n ? `Update v${current.availableVersion} available`\n : 'Update available'\n case 'downloading':\n case 'installing':\n return 'Installing update…'\n case 'failed':\n return `Version ${current.currentVersion || fallback} · Retry`\n default:\n return `${appName()} v${current.currentVersion || fallback}`\n }\n }\n\n const progressValue = () => {\n const current = state()\n if (!current?.totalBytes || !current.downloadedBytes) return null\n return Math.min(100, Math.round((current.downloadedBytes / current.totalBytes) * 100))\n }\n\n return (\n <Dialog open={open()} onOpenChange={setOpen}>\n <Show when={local.open === undefined}>\n <DialogTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Open version and updates\"\n aria-haspopup=\"dialog\"\n >\n <Show when={state()?.phase === 'available'} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <Sparkles aria-hidden=\"true\" />\n </Show>\n {triggerLabel()}\n </DialogTrigger>\n </Show>\n\n <DialogContent class={cn('max-w-3xl', local.class)}>\n <DialogHeader>\n <div class=\"flex items-center gap-3\">\n <span class=\"flex size-10 items-center justify-center rounded-xl bg-primary text-primary-foreground\">\n <Sparkles class=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <DialogTitle>Version & updates</DialogTitle>\n <DialogDescription>\n Keep {appName()} current and review what changed in each release.\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <div class=\"flex min-h-0 flex-col gap-5 overflow-y-auto p-6\">\n <section\n class=\"rounded-xl border border-border bg-background/45 p-4\"\n aria-label=\"Version status\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"text-2xs font-semibold tracking-widest text-muted-foreground uppercase\">\n Installed version\n </p>\n <p class=\"text-xl font-semibold tracking-tight\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={state()?.phase === 'current'}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\n desktop()\n ? 'Check the release channel for the latest signed build.'\n : (local.desktopOnlyMessage ??\n `Open this dialog inside the desktop app to check for updates.`)\n }\n >\n {`A newer release, v${state()?.availableVersion}, is ready.`}\n </Show>\n }\n >\n You are running the latest release.\n </Show>\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!desktop() || working()}\n onClick={() => void check()}\n >\n <Show when={working()} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Check latest version\n </Button>\n </div>\n </section>\n\n <Show when={state()?.phase === 'available' && state()?.availableVersion}>\n <section\n class=\"rounded-xl border border-primary/35 bg-primary-subtle p-4\"\n aria-label=\"Available update\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"flex items-center gap-2 text-sm font-semibold\">\n <Sparkles class=\"size-4 text-primary\" aria-hidden=\"true\" />\n Version {state()?.availableVersion} is ready\n </p>\n <p class=\"text-sm text-muted-foreground\">\n The signed installer is verified before {appName()} restarts.\n </p>\n <Show when={formatReleaseDate(state()?.releaseDate ?? null)}>\n {(released) => (\n <p class=\"text-xs text-muted-foreground\">Released {released()}</p>\n )}\n </Show>\n </div>\n <Button type=\"button\" size=\"sm\" disabled={working()} onClick={() => void install()}>\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </Button>\n </div>\n </section>\n </Show>\n\n {/*\n Progress with both numbers: a bar alone does not distinguish a stalled\n download from a slow one, and \"12.4 MB of 48.1 MB\" does.\n */}\n <Show when={state()?.phase === 'downloading' || state()?.phase === 'installing'}>\n <section class=\"grid gap-2\" aria-label=\"Update progress\">\n <Progress\n value={progressValue() ?? undefined}\n aria-label={\n state()?.phase === 'installing' ? 'Installing update' : 'Downloading update'\n }\n />\n <p class=\"text-xs text-muted-foreground\" role=\"status\">\n <Show\n when={state()?.phase === 'installing'}\n fallback={\n <Show when={progressValue() !== null} fallback=\"Downloading…\">\n {`${formatBytes(state()?.downloadedBytes ?? 0)} of ${formatBytes(\n state()?.totalBytes ?? 0\n )} · ${progressValue()}%`}\n </Show>\n }\n >\n Applying the update…\n </Show>\n </p>\n </section>\n </Show>\n\n <Show when={state()?.phase === 'current'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" aria-hidden=\"true\" />\n {appName()} is up to date.\n </p>\n </Show>\n\n <Show when={state()?.phase === 'installed'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" aria-hidden=\"true\" />\n <Show when={state()?.restartRequired} fallback=\"The update is applied.\">\n Restart {appName()} to finish the update.\n </Show>\n </p>\n </Show>\n\n <Show when={error()}>\n <p\n class=\"rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm text-destructive\"\n role=\"alert\"\n >\n {error()}\n </p>\n </Show>\n\n <Show when={notes()}>\n <section class=\"flex flex-col gap-2\" aria-labelledby=\"update-release-notes\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-release-notes\" class=\"text-sm font-semibold\">\n What changed in this release\n </h2>\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.availableVersion}\n </Badge>\n </div>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-release-notes\"\n tabindex=\"0\"\n >\n {notes()}\n </div>\n </section>\n </Show>\n\n <Show when={changelog()}>\n <section class=\"flex flex-col gap-2\" aria-labelledby=\"update-changelog\">\n <h2 id=\"update-changelog\" class=\"text-sm font-semibold\">\n Installed changelog\n </h2>\n <p class=\"text-xs text-muted-foreground\">\n A plain-text history of the installed release channel.\n </p>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-changelog\"\n tabindex=\"0\"\n >\n {changelog()}\n </div>\n </section>\n </Show>\n </div>\n\n <DialogFooter>\n <Show when={state()?.releaseUrl}>\n {(url) => (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => window.open(url(), '_blank', 'noopener,noreferrer')}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </Button>\n )}\n </Show>\n <DialogClose as={Button} variant=\"outline\" size=\"sm\">\n Close\n </DialogClose>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"update-dialog.js","names":["createEffect","createMemo","createSignal","onMount","Show","splitProps","Check","Download","ExternalLink","LoaderCircle","RefreshCw","Sparkles","Badge","Button","Dialog","DialogClose","DialogContent","DialogDescription","DialogFooter","DialogHeader","DialogTitle","DialogTrigger","Progress","cn","formatBytes","formatReleaseDate","plainTextFromMarkdown","UpdatePhase","UpdateState","Readonly","phase","currentVersion","availableVersion","changelog","releaseNotes","releaseDate","downloadedBytes","totalBytes","error","releaseUrl","restartRequired","UpdateAdapter","getStatus","Promise","check","install","expectedVersion","isDesktopRuntime","UpdateDialogProps","adapter","appName","fallbackVersion","desktopOnlyMessage","open","defaultOpen","onOpenChange","class","errorMessage","caught","fallback","Error","message","isBusy","state","UpdateDialog","props","local","uncontrolledOpen","setUncontrolledOpen","setOpen","next","undefined","desktop","setState","busy","setBusy","setError","loadStatus","active","current","checked","version","notes","value","working","triggerLabel","progressValue","Math","min","round","_$createComponent","children","when","as","variant","size","_$memo","_el$","_tmpl$","_el$2","firstChild","_el$3","nextSibling","_$insert","_el$4","_tmpl$9","_el$5","_el$6","_el$7","_el$8","_el$9","_el$0","_el$1","type","disabled","onClick","_el$10","_tmpl$2","_el$11","_el$12","_el$13","_el$14","_el$16","_el$15","_el$17","_el$18","_el$20","_el$19","released","_el$35","_tmpl$0","_el$36","_el$21","_tmpl$3","_el$22","aria-label","_el$23","_tmpl$4","_el$24","_el$25","_tmpl$5","_el$26","_tmpl$6","_el$27","_tmpl$7","_el$28","_el$29","_el$30","_el$31","_tmpl$8","_el$32","_el$33","_el$34","url","window","_$createComponent","_$memo"],"sources":["../../../../src/components/composites/update-dialog/update-dialog.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, onMount, Show, splitProps } from 'solid-js'\nimport { Check, Download, ExternalLink, LoaderCircle, RefreshCw, Sparkles } from 'lucide-solid'\nimport { Badge } from '../../ui/badge'\nimport { Button } from '../../ui/button'\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n DialogTrigger,\n} from '../../ui/dialog'\nimport { Progress } from '../../ui/progress'\nimport { cn } from '#lib/utils'\nimport { formatBytes, formatReleaseDate, plainTextFromMarkdown } from '#lib/version-notes'\n\n/**\n * UpdateDialog.\n *\n * A software-update surface: the installed version, whether a newer release\n * exists, a download that shows its progress, and the release notes for what is\n * about to arrive.\n *\n * The transport is an adapter, because an update is the one thing a component\n * library genuinely cannot implement: it is signed, downloaded and applied by the\n * application's own updater, and the mechanism differs per shell. What *is*\n * general is the phase machine and the way each phase is presented — and that is\n * where the hard-won detail lives:\n *\n * **A refused install answers with a normal payload, not a rejection.** adea's\n * shell returns a status whose phase is `failed` when a download or an extract\n * fails, rather than throwing. Without handling that, clicking install looked like\n * nothing happened at all — the button spun, the phase never changed, and the\n * error sat unread in a field nobody rendered. The `failed` phase after an install\n * is therefore surfaced as an error, explicitly.\n *\n * **Busy is derived from the snapshot as well as from the click.** An update can\n * be downloading while the dialog is open — started before it opened, or by the\n * shell's own poll — so a local `busy` flag alone would offer an enabled Install\n * button mid-download.\n *\n * **Opening the dialog loads status first, then checks.** Status paints the\n * installed version immediately; the check is a network round trip. Doing them in\n * the other order leaves the dialog blank for as long as the check takes, on a\n * dialog whose first question is always \"what am I running?\".\n *\n * **Errors are not always `Error`s.** An adapter can reject with a string, and a\n * shell can answer with an error field. Both are read, and a fallback is shown\n * rather than an empty alert.\n *\n * The adapter's `isDesktopRuntime` gate exists because an update is a desktop\n * concern: a browser build has no installer, and the honest thing to show is that\n * updates are available in the desktop application rather than a disabled button\n * with no reason.\n */\n\n/** The phase an update is in. The union is the state machine. */\nexport type UpdatePhase =\n | 'idle'\n | 'checking'\n | 'current'\n | 'available'\n | 'downloading'\n | 'installing'\n | 'installed'\n | 'failed'\n\n/** A snapshot of the updater. Every field the dialog renders, and nothing else. */\nexport type UpdateState = Readonly<{\n phase: UpdatePhase\n /** The version currently installed. */\n currentVersion: string\n /** The version that can be installed, when one can. */\n availableVersion?: string | null\n /** A changelog for the installed channel, as markdown. */\n changelog?: string\n /** Notes for the available release, as markdown. */\n releaseNotes?: string | null\n /** The available release's date, as an ISO string. */\n releaseDate?: string | null\n /** Bytes downloaded so far, during `downloading`. */\n downloadedBytes?: number\n /** Total bytes, when the server sent a length. */\n totalBytes?: number | null\n /** Set when `phase` is `failed`, and sometimes when it is not. */\n error?: string | null\n /** A URL for the release's page. */\n releaseUrl?: string | null\n /** True when the application must restart to finish. */\n restartRequired?: boolean\n}>\n\n/**\n * The updater, as the application exposes it. Three calls, each answering with a\n * full snapshot rather than a delta — an updater's state is small, and a snapshot\n * cannot drift from the shell's own view of it.\n */\nexport type UpdateAdapter = Readonly<{\n /** The current state, without contacting the release channel. */\n getStatus(): Promise<UpdateState>\n /** Ask the release channel, and answer with what it found. */\n check(): Promise<UpdateState>\n /**\n * Download and apply `expectedVersion`.\n *\n * The argument is the version the caller believes is available, so the updater\n * can refuse a request that raced a newer release rather than installing the\n * wrong one.\n */\n install(expectedVersion: string): Promise<UpdateState>\n /** False in a browser build, where there is no installer to drive. */\n isDesktopRuntime(): boolean\n}>\n\nexport type UpdateDialogProps = {\n adapter: UpdateAdapter\n /** The application's name, used in the title and the copy. */\n appName?: string\n /** Shown before the first status arrives, so the dialog is never blank. */\n fallbackVersion?: string\n /** Copy for the adapter's non-desktop case. */\n desktopOnlyMessage?: string\n /** A controlled open state. Omit for the trigger to manage its own. */\n open?: boolean\n /**\n * Start open, uncontrolled — for a caller that opens the dialog because it found\n * something (\"an update is available\") rather than because the user asked. The\n * trigger is still rendered, so the dialog is reachable again after closing.\n */\n defaultOpen?: boolean\n onOpenChange?: (open: boolean) => void\n /** Render only the panel, for a caller that provides its own dialog. */\n class?: string\n}\n\nfunction errorMessage(caught: unknown, fallback: string): string {\n if (caught instanceof Error && caught.message) return caught.message\n if (typeof caught === 'string' && caught) return caught\n return fallback\n}\n\n/**\n * The phases during which the updater is working. `downloading` and `installing`\n * are included because the shell can be in either while the dialog is open, and\n * offering an enabled Install button during a download is how a double-install\n * happens.\n */\nfunction isBusy(state: UpdateState | null): boolean {\n return (\n state?.phase === 'checking' || state?.phase === 'downloading' || state?.phase === 'installing'\n )\n}\n\nexport function UpdateDialog(props: UpdateDialogProps) {\n const [local] = splitProps(props, [\n 'adapter',\n 'appName',\n 'fallbackVersion',\n 'desktopOnlyMessage',\n 'open',\n 'defaultOpen',\n 'onOpenChange',\n 'class',\n ])\n\n const [uncontrolledOpen, setUncontrolledOpen] = createSignal(local.defaultOpen ?? false)\n const open = () => local.open ?? uncontrolledOpen()\n const setOpen = (next: boolean) => {\n if (local.open === undefined) setUncontrolledOpen(next)\n local.onOpenChange?.(next)\n }\n\n const appName = () => local.appName ?? 'the application'\n const desktop = () => local.adapter.isDesktopRuntime()\n const [state, setState] = createSignal<UpdateState | null>(null)\n const [busy, setBusy] = createSignal(false)\n const [error, setError] = createSignal('')\n\n const loadStatus = async () => {\n if (!desktop()) return\n try {\n setState(await local.adapter.getStatus())\n } catch (caught) {\n setError(errorMessage(caught, 'Version status is unavailable'))\n }\n }\n\n onMount(() => {\n if (desktop()) void loadStatus()\n })\n\n const check = async () => {\n if (!desktop()) {\n setError(local.desktopOnlyMessage ?? `Update checks are available from the desktop app.`)\n return\n }\n setBusy(true)\n setError('')\n try {\n setState(await local.adapter.check())\n } catch (caught) {\n setError(errorMessage(caught, 'Could not check for updates'))\n await loadStatus()\n } finally {\n setBusy(false)\n }\n }\n\n /**\n * Status first, then the check — and an `active` flag, because the dialog can\n * close while the check is in flight and a late `setState` would write into a\n * disposed scope.\n */\n createEffect(() => {\n if (!open() || !desktop()) return\n let active = true\n setError('')\n setBusy(true)\n void (async () => {\n try {\n const current = await local.adapter.getStatus()\n if (!active) return\n setState(current)\n const checked = await local.adapter.check()\n if (active) setState(checked)\n } catch (caught) {\n if (active) setError(errorMessage(caught, 'Could not check for updates'))\n } finally {\n if (active) setBusy(false)\n }\n })()\n return () => {\n active = false\n }\n })\n\n const install = async () => {\n const version = state()?.availableVersion\n if (!version) return\n setBusy(true)\n setError('')\n try {\n const next = await local.adapter.install(version)\n setState(next)\n // A refused install answers with a normal payload whose phase is `failed`.\n // Without this the click looked like nothing happened.\n if (next.phase === 'failed') {\n setError(errorMessage(next.error, 'Update installation failed'))\n }\n } catch (caught) {\n setError(errorMessage(caught, 'Update installation failed'))\n await loadStatus()\n } finally {\n setBusy(false)\n }\n }\n\n const changelog = createMemo(() => plainTextFromMarkdown(state()?.changelog ?? ''))\n const notes = () => {\n const value = state()?.releaseNotes\n return value ? plainTextFromMarkdown(value) : ''\n }\n const working = () => busy() || isBusy(state())\n\n const triggerLabel = () => {\n const current = state()\n const fallback = `v${local.fallbackVersion ?? '0.1.0'}`\n if (!current) return `${appName()} ${fallback}`\n switch (current.phase) {\n case 'checking':\n return 'Checking for updates…'\n case 'available':\n return current.availableVersion\n ? `Update v${current.availableVersion} available`\n : 'Update available'\n case 'downloading':\n case 'installing':\n return 'Installing update…'\n case 'failed':\n return `Version ${current.currentVersion || fallback} · Retry`\n default:\n return `${appName()} v${current.currentVersion || fallback}`\n }\n }\n\n const progressValue = () => {\n const current = state()\n if (!current?.totalBytes || !current.downloadedBytes) return null\n return Math.min(100, Math.round((current.downloadedBytes / current.totalBytes) * 100))\n }\n\n return (\n <Dialog open={open()} onOpenChange={setOpen}>\n <Show when={local.open === undefined}>\n <DialogTrigger\n as={Button}\n variant=\"ghost\"\n size=\"sm\"\n aria-label=\"Open version and updates\"\n aria-haspopup=\"dialog\"\n >\n <Show when={state()?.phase === 'available'} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <Sparkles aria-hidden=\"true\" />\n </Show>\n {triggerLabel()}\n </DialogTrigger>\n </Show>\n\n <DialogContent class={cn('max-w-3xl', local.class)}>\n <DialogHeader>\n <div class=\"flex items-center gap-3\">\n <span class=\"flex size-10 items-center justify-center rounded-xl bg-primary text-primary-foreground\">\n <Sparkles class=\"size-5\" aria-hidden=\"true\" />\n </span>\n <div>\n <DialogTitle>Version & updates</DialogTitle>\n <DialogDescription>\n Keep {appName()} current and review what changed in each release.\n </DialogDescription>\n </div>\n </div>\n </DialogHeader>\n\n <div class=\"flex min-h-0 flex-col gap-5 overflow-y-auto p-6\">\n <section\n class=\"rounded-xl border border-border bg-background/45 p-4\"\n aria-label=\"Version status\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"text-2xs font-semibold tracking-widest text-muted-foreground uppercase\">\n Installed version\n </p>\n <p class=\"text-xl font-semibold tracking-tight\">\n v{state()?.currentVersion || local.fallbackVersion || '0.1.0'}\n </p>\n <p class=\"text-sm text-muted-foreground\">\n <Show\n when={state()?.phase === 'current'}\n fallback={\n <Show\n when={state()?.phase === 'available' && state()?.availableVersion}\n fallback={\n desktop()\n ? 'Check the release channel for the latest signed build.'\n : (local.desktopOnlyMessage ??\n `Open this dialog inside the desktop app to check for updates.`)\n }\n >\n {`A newer release, v${state()?.availableVersion}, is ready.`}\n </Show>\n }\n >\n You are running the latest release.\n </Show>\n </p>\n </div>\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!desktop() || working()}\n onClick={() => void check()}\n >\n <Show when={working()} fallback={<RefreshCw aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Check latest version\n </Button>\n </div>\n </section>\n\n <Show when={state()?.phase === 'available' && state()?.availableVersion}>\n <section\n class=\"rounded-xl border border-primary/35 bg-primary-subtle p-4\"\n aria-label=\"Available update\"\n >\n <div class=\"flex flex-wrap items-start justify-between gap-4\">\n <div class=\"flex flex-col gap-1\">\n <p class=\"flex items-center gap-2 text-sm font-semibold\">\n <Sparkles class=\"size-4 text-primary\" aria-hidden=\"true\" />\n Version {state()?.availableVersion} is ready\n </p>\n <p class=\"text-sm text-muted-foreground\">\n The signed installer is verified before {appName()} restarts.\n </p>\n <Show when={formatReleaseDate(state()?.releaseDate ?? null)}>\n {(released) => (\n <p class=\"text-xs text-muted-foreground\">Released {released()}</p>\n )}\n </Show>\n </div>\n <Button type=\"button\" size=\"sm\" disabled={working()} onClick={() => void install()}>\n <Show when={working()} fallback={<Download aria-hidden=\"true\" />}>\n <LoaderCircle class=\"animate-spin\" aria-hidden=\"true\" />\n </Show>\n Install and restart\n </Button>\n </div>\n </section>\n </Show>\n\n {/*\n Progress with both numbers: a bar alone does not distinguish a stalled\n download from a slow one, and \"12.4 MB of 48.1 MB\" does.\n */}\n <Show when={state()?.phase === 'downloading' || state()?.phase === 'installing'}>\n <section class=\"grid gap-2\" aria-label=\"Update progress\">\n <Progress\n value={progressValue() ?? undefined}\n aria-label={\n state()?.phase === 'installing' ? 'Installing update' : 'Downloading update'\n }\n />\n <p class=\"text-xs text-muted-foreground\" role=\"status\">\n <Show\n when={state()?.phase === 'installing'}\n fallback={\n <Show when={progressValue() !== null} fallback=\"Downloading…\">\n {`${formatBytes(state()?.downloadedBytes ?? 0)} of ${formatBytes(\n state()?.totalBytes ?? 0\n )} · ${progressValue()}%`}\n </Show>\n }\n >\n Applying the update…\n </Show>\n </p>\n </section>\n </Show>\n\n <Show when={state()?.phase === 'current'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" aria-hidden=\"true\" />\n {appName()} is up to date.\n </p>\n </Show>\n\n <Show when={state()?.phase === 'installed'}>\n <p class=\"flex items-center gap-2 text-sm text-success\" role=\"status\">\n <Check class=\"size-4\" aria-hidden=\"true\" />\n <Show when={state()?.restartRequired} fallback=\"The update is applied.\">\n Restart {appName()} to finish the update.\n </Show>\n </p>\n </Show>\n\n <Show when={error()}>\n <p\n class=\"rounded-lg border border-destructive/35 bg-destructive-subtle px-3 py-2 text-sm text-destructive\"\n role=\"alert\"\n >\n {error()}\n </p>\n </Show>\n\n <Show when={notes()}>\n <div class=\"flex flex-col gap-2\">\n <div class=\"flex items-center gap-2\">\n <h2 id=\"update-release-notes\" class=\"text-sm font-semibold\">\n What changed in this release\n </h2>\n <Badge variant=\"subtle\" size=\"sm\">\n v{state()?.availableVersion}\n </Badge>\n </div>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-52 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-release-notes\"\n tabindex=\"0\"\n >\n {notes()}\n </div>\n </div>\n </Show>\n\n <Show when={changelog()}>\n <div class=\"flex flex-col gap-2\">\n <h2 id=\"update-changelog\" class=\"text-sm font-semibold\">\n Installed changelog\n </h2>\n <p class=\"text-xs text-muted-foreground\">\n A plain-text history of the installed release channel.\n </p>\n {/* A long text block with no focusable descendant: it needs its own tab\n stop, named by the heading above it, or a keyboard user cannot\n scroll it (`scrollable-region-focusable`). */}\n <div\n class=\"max-h-72 overflow-y-auto rounded-xl border border-border bg-background/45 p-4 font-mono text-xs leading-5 whitespace-pre-wrap text-muted-foreground\"\n role=\"region\"\n aria-labelledby=\"update-changelog\"\n tabindex=\"0\"\n >\n {changelog()}\n </div>\n </div>\n </Show>\n </div>\n\n <DialogFooter>\n <Show when={state()?.releaseUrl}>\n {(url) => (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n // `noopener,noreferrer` because the release page is a different\n // origin and must not get a handle on this window.\n onClick={() => window.open(url(), '_blank', 'noopener,noreferrer')}\n >\n <ExternalLink aria-hidden=\"true\" />\n View releases\n </Button>\n )}\n </Show>\n <DialogClose as={Button} variant=\"outline\" size=\"sm\">\n Close\n </DialogClose>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n )\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SplitLayoutBranch } from './model';
|
|
2
|
+
export type PaneDropIntent = {
|
|
3
|
+
direction: SplitLayoutBranch['direction'];
|
|
4
|
+
placement: 'before' | 'after';
|
|
5
|
+
};
|
|
6
|
+
/** Accepted Adea closest-edge intent; invalid/outside geometry cannot authorize a move. */
|
|
7
|
+
export declare function paneDropIntent(clientX: number, clientY: number, rect: {
|
|
8
|
+
left: number;
|
|
9
|
+
top: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}): PaneDropIntent | undefined;
|
|
13
|
+
//# sourceMappingURL=drop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drop.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/drop.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAChD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,iBAAiB,CAAC,WAAW,CAAC,CAAA;IACzC,SAAS,EAAE,QAAQ,GAAG,OAAO,CAAA;CAC9B,CAAA;AACD,2FAA2F;AAC3F,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,IAAI,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACjE,cAAc,GAAG,SAAS,CAkB5B"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//#region src/components/layout/split-layout/drop.ts
|
|
2
|
+
/** Accepted Adea closest-edge intent; invalid/outside geometry cannot authorize a move. */
|
|
3
|
+
function paneDropIntent(clientX, clientY, rect) {
|
|
4
|
+
const { left, top, width, height } = rect;
|
|
5
|
+
if (![
|
|
6
|
+
clientX,
|
|
7
|
+
clientY,
|
|
8
|
+
left,
|
|
9
|
+
top,
|
|
10
|
+
width,
|
|
11
|
+
height
|
|
12
|
+
].every(Number.isFinite) || width <= 0 || height <= 0) return;
|
|
13
|
+
const x = clientX - left;
|
|
14
|
+
const y = clientY - top;
|
|
15
|
+
if (x < 0 || y < 0 || x > width || y > height) return;
|
|
16
|
+
return [
|
|
17
|
+
{
|
|
18
|
+
distance: x,
|
|
19
|
+
intent: {
|
|
20
|
+
direction: "row",
|
|
21
|
+
placement: "before"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
distance: width - x,
|
|
26
|
+
intent: {
|
|
27
|
+
direction: "row",
|
|
28
|
+
placement: "after"
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
distance: y,
|
|
33
|
+
intent: {
|
|
34
|
+
direction: "column",
|
|
35
|
+
placement: "before"
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
distance: height - y,
|
|
40
|
+
intent: {
|
|
41
|
+
direction: "column",
|
|
42
|
+
placement: "after"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
].reduce((best, next) => next.distance < best.distance ? next : best).intent;
|
|
46
|
+
}
|
|
47
|
+
//#endregion
|
|
48
|
+
export { paneDropIntent };
|
|
49
|
+
|
|
50
|
+
//# sourceMappingURL=drop.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drop.js","names":[],"sources":["../../../../src/components/layout/split-layout/drop.ts"],"sourcesContent":["import type { SplitLayoutBranch } from './model'\nexport type PaneDropIntent = {\n direction: SplitLayoutBranch['direction']\n placement: 'before' | 'after'\n}\n/** Accepted Adea closest-edge intent; invalid/outside geometry cannot authorize a move. */\nexport function paneDropIntent(\n clientX: number,\n clientY: number,\n rect: { left: number; top: number; width: number; height: number }\n): PaneDropIntent | undefined {\n const { left, top, width, height } = rect\n if (\n ![clientX, clientY, left, top, width, height].every(Number.isFinite) ||\n width <= 0 ||\n height <= 0\n )\n return\n const x = clientX - left\n const y = clientY - top\n if (x < 0 || y < 0 || x > width || y > height) return\n const candidates: { distance: number; intent: PaneDropIntent }[] = [\n { distance: x, intent: { direction: 'row', placement: 'before' } },\n { distance: width - x, intent: { direction: 'row', placement: 'after' } },\n { distance: y, intent: { direction: 'column', placement: 'before' } },\n { distance: height - y, intent: { direction: 'column', placement: 'after' } },\n ]\n return candidates.reduce((best, next) => (next.distance < best.distance ? next : best)).intent\n}\n"],"mappings":";;AAMA,SAAgB,eACd,SACA,SACA,MAC4B;CAC5B,MAAM,EAAE,MAAM,KAAK,OAAO,WAAW;CACrC,IACE,CAAC;EAAC;EAAS;EAAS;EAAM;EAAK;EAAO;CAAM,CAAC,CAAC,MAAM,OAAO,QAAQ,KACnE,SAAS,KACT,UAAU,GAEV;CACF,MAAM,IAAI,UAAU;CACpB,MAAM,IAAI,UAAU;CACpB,IAAI,IAAI,KAAK,IAAI,KAAK,IAAI,SAAS,IAAI,QAAQ;CAO/C,OAAO;EALL;GAAE,UAAU;GAAG,QAAQ;IAAE,WAAW;IAAO,WAAW;GAAS;EAAE;EACjE;GAAE,UAAU,QAAQ;GAAG,QAAQ;IAAE,WAAW;IAAO,WAAW;GAAQ;EAAE;EACxE;GAAE,UAAU;GAAG,QAAQ;IAAE,WAAW;IAAU,WAAW;GAAS;EAAE;EACpE;GAAE,UAAU,SAAS;GAAG,QAAQ;IAAE,WAAW;IAAU,WAAW;GAAQ;EAAE;CAEvE,CAAA,CAAW,QAAQ,MAAM,SAAU,KAAK,WAAW,KAAK,WAAW,OAAO,IAAK,CAAC,CAAC;AAC1F"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SplitLayoutLeaf, type SplitLayoutNode } from './model';
|
|
2
|
+
export type LayoutRect = Readonly<{
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
width: number;
|
|
6
|
+
height: number;
|
|
7
|
+
}>;
|
|
8
|
+
export type LayoutFrame<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
|
|
9
|
+
node: SplitLayoutNode<L>;
|
|
10
|
+
rect: LayoutRect;
|
|
11
|
+
}>;
|
|
12
|
+
/** Stable leaf DOM can use these fractions without being reparented into a changing nested tree. */
|
|
13
|
+
export declare function computeLayoutFrames<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): ReadonlyMap<string, LayoutFrame<L>>;
|
|
14
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/geometry.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,eAAe,EACpB,KAAK,eAAe,EACrB,MAAM,SAAS,CAAA;AAChB,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC;IAAE,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAC1F,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,eAAe,GAAG,eAAe,IAAI,QAAQ,CAAC;IAC9E,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;CACjB,CAAC,CAAA;AACF,oGAAoG;AACpG,wBAAgB,mBAAmB,CAAC,CAAC,SAAS,eAAe,EAC3D,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GACvB,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,CAoBrC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MAX_SPLIT_RATIO, MIN_SPLIT_RATIO } from "./model.js";
|
|
2
|
+
//#region src/components/layout/split-layout/geometry.ts
|
|
3
|
+
/** Stable leaf DOM can use these fractions without being reparented into a changing nested tree. */
|
|
4
|
+
function computeLayoutFrames(node) {
|
|
5
|
+
const frames = /* @__PURE__ */ new Map();
|
|
6
|
+
const visit = (current, rect) => {
|
|
7
|
+
frames.set(current.id, {
|
|
8
|
+
node: current,
|
|
9
|
+
rect
|
|
10
|
+
});
|
|
11
|
+
if (current.kind === "leaf") return;
|
|
12
|
+
const ratio = Number.isFinite(current.ratio) ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, current.ratio)) : .5;
|
|
13
|
+
const row = current.direction === "row";
|
|
14
|
+
const first = row ? {
|
|
15
|
+
...rect,
|
|
16
|
+
width: rect.width * ratio
|
|
17
|
+
} : {
|
|
18
|
+
...rect,
|
|
19
|
+
height: rect.height * ratio
|
|
20
|
+
};
|
|
21
|
+
const second = row ? {
|
|
22
|
+
...rect,
|
|
23
|
+
x: rect.x + first.width,
|
|
24
|
+
width: rect.width - first.width
|
|
25
|
+
} : {
|
|
26
|
+
...rect,
|
|
27
|
+
y: rect.y + first.height,
|
|
28
|
+
height: rect.height - first.height
|
|
29
|
+
};
|
|
30
|
+
visit(current.children[0], first);
|
|
31
|
+
visit(current.children[1], second);
|
|
32
|
+
};
|
|
33
|
+
visit(node, {
|
|
34
|
+
x: 0,
|
|
35
|
+
y: 0,
|
|
36
|
+
width: 1,
|
|
37
|
+
height: 1
|
|
38
|
+
});
|
|
39
|
+
return frames;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { computeLayoutFrames };
|
|
43
|
+
|
|
44
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","names":[],"sources":["../../../../src/components/layout/split-layout/geometry.ts"],"sourcesContent":["/*\n * Translated from Muxy/Models/Workspace/SplitNode.swift areaFrames at\n * 5c5be8697c57a2fe70cda97fdbaf7c912e2e31b6.\n * Copyright (c) 2026 Muxy. SPDX-License-Identifier: MIT. See NOTICE.\n */\nimport {\n MAX_SPLIT_RATIO,\n MIN_SPLIT_RATIO,\n type SplitLayoutLeaf,\n type SplitLayoutNode,\n} from './model'\nexport type LayoutRect = Readonly<{ x: number; y: number; width: number; height: number }>\nexport type LayoutFrame<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{\n node: SplitLayoutNode<L>\n rect: LayoutRect\n}>\n/** Stable leaf DOM can use these fractions without being reparented into a changing nested tree. */\nexport function computeLayoutFrames<L extends SplitLayoutLeaf>(\n node: SplitLayoutNode<L>\n): ReadonlyMap<string, LayoutFrame<L>> {\n const frames = new Map<string, LayoutFrame<L>>()\n const visit = (current: SplitLayoutNode<L>, rect: LayoutRect) => {\n frames.set(current.id, { node: current, rect })\n if (current.kind === 'leaf') return\n const ratio = Number.isFinite(current.ratio)\n ? Math.min(MAX_SPLIT_RATIO, Math.max(MIN_SPLIT_RATIO, current.ratio))\n : 0.5\n const row = current.direction === 'row'\n const first = row\n ? { ...rect, width: rect.width * ratio }\n : { ...rect, height: rect.height * ratio }\n const second = row\n ? { ...rect, x: rect.x + first.width, width: rect.width - first.width }\n : { ...rect, y: rect.y + first.height, height: rect.height - first.height }\n visit(current.children[0], first)\n visit(current.children[1], second)\n }\n visit(node, { x: 0, y: 0, width: 1, height: 1 })\n return frames\n}\n"],"mappings":";;;AAiBA,SAAgB,oBACd,MACqC;CACrC,MAAM,yBAAS,IAAI,IAA4B;CAC/C,MAAM,SAAS,SAA6B,SAAqB;EAC/D,OAAO,IAAI,QAAQ,IAAI;GAAE,MAAM;GAAS;EAAK,CAAC;EAC9C,IAAI,QAAQ,SAAS,QAAQ;EAC7B,MAAM,QAAQ,OAAO,SAAS,QAAQ,KAAK,IACvC,KAAK,IAAI,iBAAiB,KAAK,IAAI,iBAAiB,QAAQ,KAAK,CAAC,IAClE;EACJ,MAAM,MAAM,QAAQ,cAAc;EAClC,MAAM,QAAQ,MACV;GAAE,GAAG;GAAM,OAAO,KAAK,QAAQ;EAAM,IACrC;GAAE,GAAG;GAAM,QAAQ,KAAK,SAAS;EAAM;EAC3C,MAAM,SAAS,MACX;GAAE,GAAG;GAAM,GAAG,KAAK,IAAI,MAAM;GAAO,OAAO,KAAK,QAAQ,MAAM;EAAM,IACpE;GAAE,GAAG;GAAM,GAAG,KAAK,IAAI,MAAM;GAAQ,QAAQ,KAAK,SAAS,MAAM;EAAO;EAC5E,MAAM,QAAQ,SAAS,IAAI,KAAK;EAChC,MAAM,QAAQ,SAAS,IAAI,MAAM;CACnC;CACA,MAAM,MAAM;EAAE,GAAG;EAAG,GAAG;EAAG,OAAO;EAAG,QAAQ;CAAE,CAAC;CAC/C,OAAO;AACT"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/index.ts"],"names":[],"mappings":"AAAA,yGAAyG;AACzG,cAAc,SAAS,CAAA;AACvB,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, closePane, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, swapPanes, undoClosePane } from "./model.js";
|
|
2
|
+
import { computeLayoutFrames } from "./geometry.js";
|
|
3
|
+
import { SplitLayout } from "./split-layout.js";
|
|
4
|
+
export { MAX_LAYOUT_DEPTH, MAX_LAYOUT_LEAVES, MAX_SPLIT_RATIO, MIN_SPLIT_RATIO, SplitLayout, closePane, computeLayoutFrames, countLeaves, createLayoutState, focusPane, layoutDepth, listLeaves, movePane, neighborLeaf, normalizeLayout, resizeSplit, splitPane, swapPanes, undoClosePane };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/** A visual identity only. Extend this with host-owned payload; UI never interprets it. */
|
|
2
|
+
export type SplitLayoutLeaf = Readonly<{
|
|
3
|
+
kind: 'leaf';
|
|
4
|
+
id: string;
|
|
5
|
+
}>;
|
|
6
|
+
export type SplitLayoutBranch<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
|
|
7
|
+
kind: 'split';
|
|
8
|
+
id: string;
|
|
9
|
+
direction: 'row' | 'column';
|
|
10
|
+
ratio: number;
|
|
11
|
+
children: readonly [SplitLayoutNode<L>, SplitLayoutNode<L>];
|
|
12
|
+
}>;
|
|
13
|
+
export type SplitLayoutNode<L extends SplitLayoutLeaf = SplitLayoutLeaf> = L | SplitLayoutBranch<L>;
|
|
14
|
+
export declare const MAX_LAYOUT_LEAVES = 8;
|
|
15
|
+
export declare const MAX_LAYOUT_DEPTH = 8;
|
|
16
|
+
export declare const MIN_SPLIT_RATIO = 0.1;
|
|
17
|
+
export declare const MAX_SPLIT_RATIO = 0.9;
|
|
18
|
+
export type SplitLayoutState<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
|
|
19
|
+
center: SplitLayoutNode<L>;
|
|
20
|
+
focusedLeafId: string;
|
|
21
|
+
closed: readonly Readonly<{
|
|
22
|
+
center: SplitLayoutNode<L>;
|
|
23
|
+
leafId: string;
|
|
24
|
+
}>[];
|
|
25
|
+
}>;
|
|
26
|
+
export type SplitPaneInput<L extends SplitLayoutLeaf = SplitLayoutLeaf> = Readonly<{
|
|
27
|
+
direction: SplitLayoutBranch<L>['direction'];
|
|
28
|
+
placement: 'before' | 'after';
|
|
29
|
+
leaf: L;
|
|
30
|
+
splitId: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function listLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): readonly L[];
|
|
33
|
+
export declare function countLeaves<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number;
|
|
34
|
+
export declare function layoutDepth<L extends SplitLayoutLeaf>(node: SplitLayoutNode<L>): number;
|
|
35
|
+
export declare function createLayoutState<L extends SplitLayoutLeaf>(center: SplitLayoutNode<L>): SplitLayoutState<L>;
|
|
36
|
+
export declare function splitPane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, targetLeafId: string, input: SplitPaneInput<L>): SplitLayoutState<L>;
|
|
37
|
+
export declare function closePane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string, createPlaceholderLeaf: () => L): SplitLayoutState<L>;
|
|
38
|
+
export declare function undoClosePane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>): SplitLayoutState<L>;
|
|
39
|
+
export declare function focusPane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string): SplitLayoutState<L>;
|
|
40
|
+
export declare function swapPanes<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, firstLeafId: string, secondLeafId: string): SplitLayoutState<L>;
|
|
41
|
+
export declare function movePane<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string, targetLeafId: string, placement: 'before' | 'after', direction: SplitLayoutBranch<L>['direction'], splitId: string): SplitLayoutState<L>;
|
|
42
|
+
export declare function resizeSplit<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, splitId: string, ratio: number): SplitLayoutState<L>;
|
|
43
|
+
/**
|
|
44
|
+
* Pure repair walk ported from bb's size normalization: every split ratio is
|
|
45
|
+
* clamped into the normative range and a non-finite ratio falls back to an
|
|
46
|
+
* even split. Structure, IDs, and focus are untouched.
|
|
47
|
+
*/
|
|
48
|
+
export declare function normalizeLayout<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>): SplitLayoutState<L>;
|
|
49
|
+
/** The leaf immediately after (`1`) or before (`-1`) the given leaf in reading order. */
|
|
50
|
+
export declare function neighborLeaf<L extends SplitLayoutLeaf>(state: SplitLayoutState<L>, leafId: string, step: 1 | -1): L | undefined;
|
|
51
|
+
//# sourceMappingURL=model.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../../src/components/layout/split-layout/model.ts"],"names":[],"mappings":"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"}
|