From 510fd8cbe53abb39cba2c7cbaaefcf2783dc0066 Mon Sep 17 00:00:00 2001 From: "netop://ウィビ" Date: Fri, 24 Apr 2026 16:37:33 -0700 Subject: Implement v0.6-1.0: shortcuts, format, export/import, splitter, timing, APQ - v0.6: matchShortcut + format(); Cmd+Shift+Enter/W/F + Cmd+Alt+arrows - v0.7: SessionStore.exportAll/importTabs with version-1 validator - v0.8: Splitter component + four resize handles persisted under layout.* - v0.10: createApqFetcher (HTTP-only) wrapping shared http-body helpers - Drop .svelte re-exports from index.ts for multi-entry JSR/npm publishing --- source/library/components/HistoryPanel.svelte | 106 ++++++++++++++++++++-- source/library/components/ResultViewer.svelte | 51 ++++++++++- source/library/components/Splitter.svelte | 123 ++++++++++++++++++++++++++ source/library/components/Toolbar.svelte | 5 ++ 4 files changed, 277 insertions(+), 8 deletions(-) create mode 100644 source/library/components/Splitter.svelte (limited to 'source/library/components') diff --git a/source/library/components/HistoryPanel.svelte b/source/library/components/HistoryPanel.svelte index b7f5c4c..01f397a 100644 --- a/source/library/components/HistoryPanel.svelte +++ b/source/library/components/HistoryPanel.svelte @@ -3,13 +3,29 @@ type Props = { entries: HistoryEntry[]; + notice?: string | null; onClear: () => void; + onDismissNotice?: () => void; + onExport?: () => void; onFavorite: (id: string) => void; + onImport?: (file: File) => void; onLoad: (id: string, inNewTab: boolean) => void; onRemove: (id: string) => void; }; - let { entries, onClear, onFavorite, onLoad, onRemove }: Props = $props(); + let { + entries, + notice = null, + onClear, + onDismissNotice, + onExport, + onFavorite, + onImport, + onLoad, + onRemove + }: Props = $props(); + + let fileInput = $state(null); const sorted = $derived([...entries].sort((a, b) => { if (a.favorite !== b.favorite) @@ -33,6 +49,20 @@ onLoad(id, event.shiftKey); } } + + function onImportClick() { + fileInput?.click(); + } + + function onFileChange(event: Event) { + const input = event.currentTarget as HTMLInputElement; + const file = input.files?.[0]; + + if (file) + onImport?.(file); + + input.value = ""; + }
-
- History - {#if entries.length > 0} - +
+
+ History +
+ {#if onExport} + + {/if} + {#if onImport} + + + {/if} + {#if entries.length > 0} + + {/if} +
+
+ {#if notice} +
+ {notice} + {#if onDismissNotice} + + {/if} +
{/if}
diff --git a/source/library/components/ResultViewer.svelte b/source/library/components/ResultViewer.svelte index 083828d..d277ec8 100644 --- a/source/library/components/ResultViewer.svelte +++ b/source/library/components/ResultViewer.svelte @@ -2,14 +2,50 @@ import Editor from "./Editor.svelte"; import type { Extension } from "@codemirror/state"; import type { Snippet } from "svelte"; + import type { TabTiming } from "../state/session.svelte.ts"; type Props = { footer?: Snippet<[{ result: string }]>; + streamIntervals?: number[]; theme?: Extension; + timing?: TabTiming | null; value: string; }; - let { footer, theme, value }: Props = $props(); + let { + footer, + streamIntervals = [], + theme, + timing = null, + value + }: Props = $props(); + + function median(values: number[]): number { + if (values.length === 0) + return 0; + + const sorted = [...values].sort((a, b) => a - b); + const mid = Math.floor(sorted.length / 2); + + return sorted.length % 2 === 0 ? + (sorted[mid - 1] + sorted[mid]) / 2 : + sorted[mid]; + } + + let metadata = $derived.by(() => { + if (!timing) + return null; + + if (streamIntervals.length > 0) { + const medianMs = Math.round(median(streamIntervals)); + const messages = streamIntervals.length + 1; + return `→ ${messages} messages · median ${medianMs}ms`; + } + + const totalMs = Math.round(timing.endMs - timing.startMs); + const firstByteMs = Math.round(timing.firstByteMs - timing.startMs); + return `→ ${totalMs}ms · first byte ${firstByteMs}ms`; + }); function noop(_v: string) {} @@ -17,7 +53,7 @@ + + diff --git a/source/library/components/Toolbar.svelte b/source/library/components/Toolbar.svelte index 8c75668..9882b7d 100644 --- a/source/library/components/Toolbar.svelte +++ b/source/library/components/Toolbar.svelte @@ -8,6 +8,7 @@ docsOpen?: boolean; extras?: Snippet; historyOpen?: boolean; + onFormat?: () => void; onRun: () => void; onSelectOperation?: (name: string | null) => void; onToggleDocs?: () => void; @@ -24,6 +25,7 @@ docsOpen = false, extras, historyOpen = false, + onFormat, onRun, onSelectOperation, onToggleDocs, @@ -128,6 +130,9 @@ {/each} {/if} + {#if onFormat} + + {/if} {#if extras}{@render extras()}{/if} ⌘/Ctrl + Enter {#if schemaLoading} -- cgit v1.2.3