@lapismd/design-core 0.1.0 → 0.2.0
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/CHANGELOG.md +44 -0
- package/LICENSE.md +3 -3
- package/README.md +21 -1
- package/package.json +34 -15
- package/spec/src/styling-and-themes.md +11 -9
- package/src/shared/ai/chat.css +25 -1
- package/src/shared/ai/chat.ts +12 -0
- package/src/shared/ai/composer-input/ComposerInput.svelte +3 -2
- package/src/shared/ai/context.svelte.ts +2 -0
- package/src/shared/ai/experimental/reaction-bar/ReactionBar.svelte +1 -1
- package/src/shared/ai/layout/Layout.svelte +116 -10
- package/src/shared/ai/message-list/MessageList.svelte +123 -43
- package/src/shared/ai/message-list/MessageListHistoryFixture.svelte +112 -0
- package/src/shared/ai/message-list/MessageListLegacyFixture.svelte +52 -0
- package/src/shared/ai/message-list/VirtualizedMessageRows.svelte +299 -0
- package/src/shared/ai/message-list/position.ts +7 -0
- package/src/shared/ai/new-messages.svelte.ts +24 -5
- package/src/shared/ai/stream-scroll.svelte.ts +135 -6
- package/src/shared/ai/tool-call-detail/ToolCallDetail.svelte +71 -0
- package/src/shared/ai/tool-call-detail/tool-call-detail.ts +209 -0
- package/src/shared/ai/tool-calls/ToolCalls.svelte +1 -1
- package/src/shared/ai/trigger-menu.ts +2 -3
- package/src/shared/diff/core/merge/actions.ts +4 -3
- package/src/shared/diff/core/merge/render-model.ts +14 -10
- package/src/shared/diff/file-diff/FileDiff.css +68 -1
- package/src/shared/diff/file-diff/FileDiff.svelte +27 -14
- package/src/shared/diff/file-diff/FileDiffComposer.svelte +98 -16
- package/src/shared/diff/file-diff/index.ts +2 -0
- package/src/shared/diff/file-diff/types.ts +13 -0
- package/src/shared/diff/file-listing/FileListingViewModeToggle.css +42 -5
- package/src/shared/diff/index.ts +2 -0
- package/src/shared/diff/merge-editor/MergeEditor.svelte +2 -7
- package/src/shared/filter/filter-query/filters.ts +2 -2
- package/src/shared/filter/filter-query/parser.ts +9 -8
- package/src/shared/filter/power-search/PowerSearch.svelte +1 -1
- package/src/shared/filter/search-filter-bar/SearchFilterBar.svelte +2 -2
- package/src/shared/filter/search-filter-bar/SearchFilterInput.svelte +1 -1
- package/src/shared/filter/search-filter-bar/SearchFilterPredicateEditor.svelte +1 -1
- package/src/shared/filter/search-filter-bar/search-filter-syntax.ts +1 -1
- package/src/shared/forms/autocomplete-input/AutocompleteInput.svelte +19 -7
- package/src/shared/forms/confirm-button/ConfirmButton.svelte +38 -0
- package/src/shared/forms/confirm-button/ConfirmButtonExample.svelte +14 -0
- package/src/shared/forms/confirm-button/index.ts +1 -0
- package/src/shared/forms/confirm-dialog/ConfirmDialog.css +21 -0
- package/src/shared/forms/confirm-dialog/ConfirmDialog.svelte +116 -0
- package/src/shared/forms/confirm-dialog/ConfirmDialogExample.svelte +17 -0
- package/src/shared/forms/confirm-dialog/index.ts +2 -0
- package/src/shared/forms/dialog-frame/DialogFrame.css +130 -0
- package/src/shared/forms/dialog-frame/DialogFrame.svelte +108 -0
- package/src/shared/forms/dialog-frame/DialogFrameExample.svelte +37 -0
- package/src/shared/forms/dialog-frame/index.ts +6 -0
- package/src/shared/forms/dialog.ts +8 -0
- package/src/shared/forms/index.ts +11 -0
- package/src/shared/shadcn/button/button.svelte +4 -0
- package/src/shared/shadcn/code-block/highlightRanges.ts +10 -6
- package/src/shared/shadcn/code-block/syntax/defineSyntaxTheme.ts +4 -2
- package/src/shared/shadcn/code-block/tokenizer.ts +3 -3
- package/src/shared/shadcn/command/command-dialog.svelte +1 -1
- package/src/shared/shadcn/command/command-group.svelte +3 -1
- package/src/shared/shadcn/command-view/command-view.svelte +1 -6
- package/src/shared/shadcn/dialog/dialog-content.svelte +3 -4
- package/src/shared/shadcn/dialog/dialog-portal.svelte +10 -2
- package/src/shared/shadcn/dropdown-menu/dropdown-menu-checkbox-item.svelte +0 -691
- package/src/shared/shadcn/dropdown-menu/dropdown-menu-content.svelte +691 -0
- package/src/shared/shadcn/dropdown-menu/dropdown-menu-radio-group.svelte +1 -1
- package/src/shared/shadcn/scroll-area/scroll-area.svelte +2 -0
- package/src/shared/shadcn/spinner/spinner.svelte +3 -3
- package/src/shared/shadcn/tooltip/tooltip-content.svelte +10 -4
- package/src/shared/shadcn/tooltip/tooltip.tokens.ts +4 -0
- package/src/shared/shell/app-shell/AppShell.css +112 -32
- package/src/shared/shell/app-shell/AppShell.demo.css +37 -0
- package/src/shared/shell/app-shell/AppShellBodyContent.svelte +5 -1
- package/src/shared/shell/app-shell/AppShellBodySidebar.svelte +5 -1
- package/src/shared/shell/app-shell/AppShellMain.svelte +12 -1
- package/src/shared/shell/app-shell/AppShellMobilePanelSelector.svelte +1 -1
- package/src/shared/shell/app-shell/AppShellRoot.svelte +27 -13
- package/src/shared/shell/app-shell/AppShellSidebar.svelte +121 -19
- package/src/shared/shell/app-shell/AppShellSidebarClose.svelte +17 -17
- package/src/shared/shell/app-shell/AppShellSidebarToggle.svelte +38 -58
- package/src/shared/shell/app-shell/AppShellSurfaceLayer.svelte +173 -0
- package/src/shared/shell/app-shell/AppShellSurfaceLayerBody.svelte +32 -0
- package/src/shared/shell/app-shell/AppShellSurfaceLayerHeader.svelte +26 -0
- package/src/shared/shell/app-shell/app-shell-context.svelte.ts +6 -3
- package/src/shared/shell/app-shell/app-shell-controller.svelte.ts +269 -11
- package/src/shared/shell/app-shell/app-shell-sidebar-context.svelte.ts +1 -0
- package/src/shared/shell/app-shell/examples/AppShellBodyDemo.svelte +4 -0
- package/src/shared/shell/app-shell/examples/AppShellCompleteDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellConversationDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellExamples.css +624 -0
- package/src/shared/shell/app-shell/examples/AppShellFilesSidebarDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellMarkdownDocumentDemo.svelte +6 -1
- package/src/shared/shell/app-shell/examples/AppShellMarkdownFilesDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellModeSwitchDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellMultiPanelDemo.svelte +187 -0
- package/src/shared/shell/app-shell/examples/AppShellProjectSidebarDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellSidebarDemo.svelte +1 -0
- package/src/shared/shell/app-shell/examples/AppShellToolbarDemo.svelte +1 -0
- package/src/shared/shell/app-shell/index.ts +14 -0
- package/src/shared/shell/shell.tokens.css +2 -0
- package/src/shared/shell/shell.tokens.ts +5 -0
- package/src/shared/workspace/menu/WorkspaceContextMenuItems.svelte +2 -1
- package/src/shared/workspace/menu/WorkspaceMenuItems.svelte +2 -1
- package/src/shared/workspace/problems/ProblemsQuickFix.svelte +1 -4
- package/src/shared/workspace/settings/AppSettingsContent.svelte +6 -0
- package/src/shared/workspace/settings/AppSettingsNavigation.svelte +7 -0
- package/src/shared/workspace/settings/CopyableValue.svelte +81 -0
- package/src/shared/workspace/settings/DeclarativeSettingsSection.svelte +20 -0
- package/src/shared/workspace/settings/WorkspaceSettingField.svelte +110 -41
- package/src/shared/workspace/settings/WorkspaceSettingMultiSelect.svelte +7 -6
- package/src/shared/workspace/settings/WorkspaceSettingObjectTable.svelte +187 -23
- package/src/shared/workspace/settings/WorkspaceSettingSelect.svelte +29 -29
- package/src/shared/workspace/settings/WorkspaceSettings.css +190 -4
- package/src/shared/workspace/settings/WorkspaceSettingsStoryCustomField.svelte +2 -2
- package/src/shared/workspace/settings/WorkspaceSettingsStoryCustomPage.svelte +13 -0
- package/src/shared/workspace/settings/WorkspaceSettingsSurface.svelte +50 -0
- package/src/shared/workspace/settings/configuration.ts +5 -0
- package/src/shared/workspace/settings/copyable-value.ts +10 -0
- package/src/shared/workspace/settings/index.ts +5 -0
- package/src/shared/workspace/settings/settings-controller.svelte.ts +349 -15
- package/src/shared/workspace/settings/types.ts +90 -8
- package/src/storybook/app-shell-reference/AppShellTwoExpandedSidebarsReference.css +25 -0
- package/src/storybook/app-shell-reference/AppShellTwoExpandedSidebarsReference.svelte +48 -0
- package/src/storybook/app-shell-reference/index.ts +1 -0
- package/src/theme.css +10 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Button, type ButtonProps } from "../../shadcn/button/index.js";
|
|
3
|
+
import ConfirmDialog, {
|
|
4
|
+
type ConfirmDialogProps,
|
|
5
|
+
} from "../confirm-dialog/ConfirmDialog.svelte";
|
|
6
|
+
import type { Snippet } from "svelte";
|
|
7
|
+
let {
|
|
8
|
+
children,
|
|
9
|
+
disabled = false,
|
|
10
|
+
variant,
|
|
11
|
+
size = "default",
|
|
12
|
+
...confirmation
|
|
13
|
+
}: Omit<ConfirmDialogProps, "open"> & {
|
|
14
|
+
children: Snippet;
|
|
15
|
+
disabled?: boolean;
|
|
16
|
+
variant?: ButtonProps["variant"];
|
|
17
|
+
size?: ButtonProps["size"];
|
|
18
|
+
} = $props();
|
|
19
|
+
let open = $state(false);
|
|
20
|
+
let trigger = $state<HTMLButtonElement | null>(null);
|
|
21
|
+
</script>
|
|
22
|
+
|
|
23
|
+
<Button
|
|
24
|
+
bind:ref={trigger}
|
|
25
|
+
{disabled}
|
|
26
|
+
{size}
|
|
27
|
+
variant={variant ?? (confirmation.destructive ? "destructive" : "outline")}
|
|
28
|
+
onclick={() => (open = true)}>{@render children()}</Button
|
|
29
|
+
>
|
|
30
|
+
<ConfirmDialog
|
|
31
|
+
{...confirmation}
|
|
32
|
+
bind:open
|
|
33
|
+
onCloseAutoFocus={(event) => {
|
|
34
|
+
event.preventDefault();
|
|
35
|
+
trigger?.focus();
|
|
36
|
+
confirmation.onCloseAutoFocus?.(event);
|
|
37
|
+
}}
|
|
38
|
+
/>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ConfirmButton } from "@lapismd/design-core/forms/dialog";
|
|
3
|
+
let count = $state(0);
|
|
4
|
+
</script>
|
|
5
|
+
|
|
6
|
+
<ConfirmButton
|
|
7
|
+
title="Clear history?"
|
|
8
|
+
description="Remove saved history from this device."
|
|
9
|
+
destructive
|
|
10
|
+
onConfirm={() => {
|
|
11
|
+
count += 1;
|
|
12
|
+
}}>Clear history</ConfirmButton
|
|
13
|
+
>
|
|
14
|
+
<p>Cleared {count} time</p>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ConfirmButton } from "./ConfirmButton.svelte";
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
[data-ui-confirm-dialog][data-ui-part="alert-dialog-content"] {
|
|
2
|
+
width: min(28rem, calc(100vw - 2rem));
|
|
3
|
+
max-width: 28rem;
|
|
4
|
+
max-height: calc(100dvh - 2rem);
|
|
5
|
+
overflow: auto;
|
|
6
|
+
font-family: var(--font-sans, sans-serif);
|
|
7
|
+
font-size: var(--ui-dialog-frame-font-size, 0.875rem);
|
|
8
|
+
}
|
|
9
|
+
[data-ui-confirm-dialog] [data-slot="alert-dialog-title"] {
|
|
10
|
+
font-size: var(--ui-dialog-frame-title-size, 1.125rem);
|
|
11
|
+
}
|
|
12
|
+
[data-ui-confirm-dialog] [data-slot="alert-dialog-footer"] {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: row;
|
|
15
|
+
justify-content: flex-end;
|
|
16
|
+
gap: 0.5rem;
|
|
17
|
+
}
|
|
18
|
+
[data-ui-confirm-dialog] [role="alert"] {
|
|
19
|
+
color: var(--destructive);
|
|
20
|
+
margin: 0;
|
|
21
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { ComponentProps, Snippet } from "svelte";
|
|
3
|
+
import type * as AlertDialogTypes from "../../shadcn/alert-dialog/index.js";
|
|
4
|
+
export interface ConfirmDialogProps {
|
|
5
|
+
open?: boolean;
|
|
6
|
+
title: string | Snippet;
|
|
7
|
+
description: string | Snippet;
|
|
8
|
+
confirmLabel?: string | Snippet;
|
|
9
|
+
children?: Snippet;
|
|
10
|
+
confirmDisabled?: boolean;
|
|
11
|
+
cancelLabel?: string;
|
|
12
|
+
destructive?: boolean;
|
|
13
|
+
pending?: boolean;
|
|
14
|
+
error?: string;
|
|
15
|
+
/** Resolve false to retain an externally reported validation error. Reject to show a retryable error. */
|
|
16
|
+
onConfirm: () => void | boolean | Promise<void | boolean>;
|
|
17
|
+
onCancel?: () => void;
|
|
18
|
+
onOpenChange?: (open: boolean) => void;
|
|
19
|
+
onCloseAutoFocus?: ComponentProps<
|
|
20
|
+
typeof AlertDialogTypes.Content
|
|
21
|
+
>["onCloseAutoFocus"];
|
|
22
|
+
portalProps?: ComponentProps<
|
|
23
|
+
typeof AlertDialogTypes.Content
|
|
24
|
+
>["portalProps"];
|
|
25
|
+
}
|
|
26
|
+
</script>
|
|
27
|
+
|
|
28
|
+
<script lang="ts">
|
|
29
|
+
import * as AlertDialog from "../../shadcn/alert-dialog/index.js";
|
|
30
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
31
|
+
import "./ConfirmDialog.css";
|
|
32
|
+
let {
|
|
33
|
+
open = $bindable(false),
|
|
34
|
+
title,
|
|
35
|
+
description,
|
|
36
|
+
confirmLabel = "Confirm",
|
|
37
|
+
cancelLabel = "Cancel",
|
|
38
|
+
children,
|
|
39
|
+
confirmDisabled = false,
|
|
40
|
+
destructive = false,
|
|
41
|
+
pending = false,
|
|
42
|
+
error = "",
|
|
43
|
+
onConfirm,
|
|
44
|
+
onCancel,
|
|
45
|
+
onOpenChange,
|
|
46
|
+
onCloseAutoFocus,
|
|
47
|
+
portalProps,
|
|
48
|
+
}: ConfirmDialogProps = $props();
|
|
49
|
+
let submitting = $state(false);
|
|
50
|
+
let failure = $state("");
|
|
51
|
+
const busy = $derived(pending || submitting);
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!open) failure = "";
|
|
54
|
+
});
|
|
55
|
+
function changeOpen(next: boolean) {
|
|
56
|
+
if (busy) return;
|
|
57
|
+
open = next;
|
|
58
|
+
onOpenChange?.(next);
|
|
59
|
+
if (!next) onCancel?.();
|
|
60
|
+
}
|
|
61
|
+
async function confirm() {
|
|
62
|
+
if (busy) return;
|
|
63
|
+
submitting = true;
|
|
64
|
+
failure = "";
|
|
65
|
+
try {
|
|
66
|
+
if ((await onConfirm()) !== false) {
|
|
67
|
+
open = false;
|
|
68
|
+
onOpenChange?.(false);
|
|
69
|
+
}
|
|
70
|
+
} catch (cause) {
|
|
71
|
+
failure =
|
|
72
|
+
cause instanceof Error
|
|
73
|
+
? cause.message
|
|
74
|
+
: "The action failed. Please try again.";
|
|
75
|
+
} finally {
|
|
76
|
+
submitting = false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
</script>
|
|
80
|
+
|
|
81
|
+
<AlertDialog.Root {open} onOpenChange={changeOpen}>
|
|
82
|
+
<AlertDialog.Content
|
|
83
|
+
data-ui-confirm-dialog=""
|
|
84
|
+
{...portalProps === undefined ? {} : { portalProps }}
|
|
85
|
+
{...onCloseAutoFocus === undefined ? {} : { onCloseAutoFocus }}
|
|
86
|
+
onEscapeKeydown={(event) => {
|
|
87
|
+
if (busy) event.preventDefault();
|
|
88
|
+
}}
|
|
89
|
+
>
|
|
90
|
+
<AlertDialog.Header>
|
|
91
|
+
<AlertDialog.Title
|
|
92
|
+
>{#if typeof title === "string"}{title}{:else}{@render title()}{/if}</AlertDialog.Title
|
|
93
|
+
>
|
|
94
|
+
<AlertDialog.Description
|
|
95
|
+
>{#if typeof description === "string"}{description}{:else}{@render description()}{/if}</AlertDialog.Description
|
|
96
|
+
>
|
|
97
|
+
</AlertDialog.Header>
|
|
98
|
+
{@render children?.()}
|
|
99
|
+
{#if failure || error}<p role="alert">{failure || error}</p>{/if}
|
|
100
|
+
<AlertDialog.Footer>
|
|
101
|
+
<Button
|
|
102
|
+
variant="outline"
|
|
103
|
+
disabled={busy}
|
|
104
|
+
onclick={() => changeOpen(false)}
|
|
105
|
+
autofocus>{cancelLabel}</Button
|
|
106
|
+
>
|
|
107
|
+
<Button
|
|
108
|
+
variant={destructive ? "destructive" : "default"}
|
|
109
|
+
disabled={busy || confirmDisabled}
|
|
110
|
+
aria-busy={busy}
|
|
111
|
+
onclick={confirm}
|
|
112
|
+
>{#if busy}Working…{:else if typeof confirmLabel === "string"}{confirmLabel}{:else}{@render confirmLabel()}{/if}</Button
|
|
113
|
+
>
|
|
114
|
+
</AlertDialog.Footer>
|
|
115
|
+
</AlertDialog.Content>
|
|
116
|
+
</AlertDialog.Root>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ConfirmDialog } from "@lapismd/design-core/forms/dialog";
|
|
3
|
+
let open = $state(true);
|
|
4
|
+
let removed = $state(false);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<ConfirmDialog
|
|
8
|
+
bind:open
|
|
9
|
+
title="Remove record?"
|
|
10
|
+
description="Remove the selected record."
|
|
11
|
+
confirmLabel="Remove"
|
|
12
|
+
destructive
|
|
13
|
+
onConfirm={async () => {
|
|
14
|
+
removed = true;
|
|
15
|
+
}}
|
|
16
|
+
/>
|
|
17
|
+
<p>{removed ? "Removed" : "Available"}</p>
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
[data-ui-dialog-frame][data-ui-part="dialog-content"] {
|
|
2
|
+
--ui-dialog-frame-width: 40rem;
|
|
3
|
+
display: flex;
|
|
4
|
+
flex-direction: column;
|
|
5
|
+
gap: 0;
|
|
6
|
+
width: min(var(--ui-dialog-frame-width), calc(100vw - 2rem));
|
|
7
|
+
max-width: calc(100vw - 2rem);
|
|
8
|
+
max-height: calc(100dvh - 2rem);
|
|
9
|
+
padding: 0;
|
|
10
|
+
overflow: hidden;
|
|
11
|
+
font-family: var(--font-sans, sans-serif);
|
|
12
|
+
font-size: var(--ui-dialog-frame-font-size, 0.875rem);
|
|
13
|
+
line-height: 1.5;
|
|
14
|
+
}
|
|
15
|
+
[data-ui-dialog-frame][data-frame-size="compact"] {
|
|
16
|
+
--ui-dialog-frame-width: 28rem;
|
|
17
|
+
}
|
|
18
|
+
[data-ui-dialog-frame][data-frame-size="wide"] {
|
|
19
|
+
--ui-dialog-frame-width: 68rem;
|
|
20
|
+
}
|
|
21
|
+
[data-ui-dialog-frame][data-frame-size="large"] {
|
|
22
|
+
--ui-dialog-frame-width: 56rem;
|
|
23
|
+
max-height: min(42rem, calc(100dvh - 2rem));
|
|
24
|
+
}
|
|
25
|
+
[data-ui-dialog-frame][data-frame-surface="inset"] {
|
|
26
|
+
background: var(--muted);
|
|
27
|
+
}
|
|
28
|
+
[data-ui-dialog-frame] > [data-ui-part="dialog-frame-header"] {
|
|
29
|
+
display: flex;
|
|
30
|
+
align-items: flex-start;
|
|
31
|
+
gap: 1rem;
|
|
32
|
+
flex: none;
|
|
33
|
+
padding: 1.25rem 1.5rem;
|
|
34
|
+
}
|
|
35
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-heading"] {
|
|
36
|
+
flex: 1;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
display: grid;
|
|
39
|
+
gap: 0.5rem;
|
|
40
|
+
}
|
|
41
|
+
[data-ui-dialog-frame] [data-slot="dialog-title"] {
|
|
42
|
+
font-size: var(--ui-dialog-frame-title-size, 1.125rem);
|
|
43
|
+
line-height: 1.4;
|
|
44
|
+
font-weight: 600;
|
|
45
|
+
}
|
|
46
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-close"] {
|
|
47
|
+
flex: none;
|
|
48
|
+
}
|
|
49
|
+
[data-ui-dialog-frame][data-frame-surface="inset"]
|
|
50
|
+
> [data-ui-part="dialog-frame-header"] {
|
|
51
|
+
background: var(--muted);
|
|
52
|
+
}
|
|
53
|
+
[data-ui-dialog-frame][data-frame-surface="inset"]
|
|
54
|
+
[data-ui-part="dialog-frame-close"]:is(:hover, :focus-visible) {
|
|
55
|
+
background: var(--background);
|
|
56
|
+
color: var(--foreground);
|
|
57
|
+
}
|
|
58
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-layout"] {
|
|
59
|
+
display: flex;
|
|
60
|
+
flex: 1;
|
|
61
|
+
min-height: 0;
|
|
62
|
+
min-width: 0;
|
|
63
|
+
}
|
|
64
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-sidebar"] {
|
|
65
|
+
flex: 0 0 10rem;
|
|
66
|
+
padding: 0.5rem 1rem 1rem;
|
|
67
|
+
background: var(--muted);
|
|
68
|
+
}
|
|
69
|
+
[data-ui-dialog-frame][data-frame-surface="inset"]
|
|
70
|
+
[data-ui-part="dialog-frame-sidebar"] {
|
|
71
|
+
flex-basis: 13rem;
|
|
72
|
+
}
|
|
73
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-main"] {
|
|
74
|
+
display: flex;
|
|
75
|
+
flex-direction: column;
|
|
76
|
+
flex: 1;
|
|
77
|
+
min-width: 0;
|
|
78
|
+
min-height: 0;
|
|
79
|
+
}
|
|
80
|
+
[data-ui-dialog-frame][data-frame-surface="inset"]
|
|
81
|
+
[data-ui-part="dialog-frame-main"] {
|
|
82
|
+
margin: 0 1rem 1rem 0;
|
|
83
|
+
border-radius: 1rem;
|
|
84
|
+
background: var(--background);
|
|
85
|
+
overflow: hidden;
|
|
86
|
+
}
|
|
87
|
+
[data-ui-dialog-frame] .ui-dialog-frame-scroll {
|
|
88
|
+
flex: 1;
|
|
89
|
+
min-height: 0;
|
|
90
|
+
min-width: 0;
|
|
91
|
+
overflow: hidden;
|
|
92
|
+
display: flex;
|
|
93
|
+
flex-direction: column;
|
|
94
|
+
}
|
|
95
|
+
[data-ui-dialog-frame]
|
|
96
|
+
.ui-dialog-frame-scroll
|
|
97
|
+
> [data-ui-part="scroll-area-viewport"] {
|
|
98
|
+
flex: 1;
|
|
99
|
+
height: auto;
|
|
100
|
+
min-height: 0;
|
|
101
|
+
}
|
|
102
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-body"] {
|
|
103
|
+
padding: 0.5rem 1.5rem 1.5rem;
|
|
104
|
+
}
|
|
105
|
+
[data-ui-dialog-frame][data-frame-padding="none"]
|
|
106
|
+
[data-ui-part="dialog-frame-body"] {
|
|
107
|
+
padding: 0;
|
|
108
|
+
}
|
|
109
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-footer"] {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex: none;
|
|
112
|
+
flex-direction: row;
|
|
113
|
+
justify-content: flex-end;
|
|
114
|
+
flex-wrap: wrap;
|
|
115
|
+
gap: 0.5rem;
|
|
116
|
+
padding: 1rem 1.5rem;
|
|
117
|
+
background: var(--popover);
|
|
118
|
+
}
|
|
119
|
+
@media (max-width: 40rem) {
|
|
120
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-layout"] {
|
|
121
|
+
flex-direction: column;
|
|
122
|
+
}
|
|
123
|
+
[data-ui-dialog-frame] [data-ui-part="dialog-frame-sidebar"] {
|
|
124
|
+
flex: none;
|
|
125
|
+
}
|
|
126
|
+
[data-ui-dialog-frame][data-frame-surface="inset"]
|
|
127
|
+
[data-ui-part="dialog-frame-main"] {
|
|
128
|
+
margin-left: 1rem;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { ComponentProps, Snippet } from "svelte";
|
|
3
|
+
import type * as DialogTypes from "../../shadcn/dialog/index.js";
|
|
4
|
+
|
|
5
|
+
export type DialogFrameSize = "compact" | "form" | "large" | "wide";
|
|
6
|
+
export type DialogFrameSurface = "plain" | "inset";
|
|
7
|
+
export type DialogFrameProps = Omit<
|
|
8
|
+
ComponentProps<typeof DialogTypes.Content>,
|
|
9
|
+
"children" | "title" | "showCloseButton" | "dataUiComponent" | "dataUiPart"
|
|
10
|
+
> & {
|
|
11
|
+
title: string | Snippet;
|
|
12
|
+
description?: string | Snippet;
|
|
13
|
+
descriptionHidden?: boolean;
|
|
14
|
+
size?: DialogFrameSize;
|
|
15
|
+
/** Visual treatment for the dialog shell and main content surface. */
|
|
16
|
+
surface?: DialogFrameSurface;
|
|
17
|
+
busy?: boolean;
|
|
18
|
+
closeLabel?: string;
|
|
19
|
+
sidebar?: Snippet;
|
|
20
|
+
headerActions?: Snippet;
|
|
21
|
+
footer?: Snippet;
|
|
22
|
+
children: Snippet;
|
|
23
|
+
bodyPadding?: "default" | "none";
|
|
24
|
+
};
|
|
25
|
+
</script>
|
|
26
|
+
|
|
27
|
+
<script lang="ts">
|
|
28
|
+
import * as Dialog from "../../shadcn/dialog/index.js";
|
|
29
|
+
import { Button } from "../../shadcn/button/index.js";
|
|
30
|
+
import { ScrollArea } from "../../shadcn/scroll-area/index.js";
|
|
31
|
+
import XIcon from "@lucide/svelte/icons/x";
|
|
32
|
+
import "./DialogFrame.css";
|
|
33
|
+
|
|
34
|
+
let {
|
|
35
|
+
title,
|
|
36
|
+
description,
|
|
37
|
+
descriptionHidden = false,
|
|
38
|
+
size = "form",
|
|
39
|
+
surface = "plain",
|
|
40
|
+
busy = false,
|
|
41
|
+
closeLabel = "Close",
|
|
42
|
+
sidebar,
|
|
43
|
+
headerActions,
|
|
44
|
+
footer,
|
|
45
|
+
children,
|
|
46
|
+
bodyPadding = "default",
|
|
47
|
+
ref = $bindable(null),
|
|
48
|
+
onInteractOutside,
|
|
49
|
+
onEscapeKeydown,
|
|
50
|
+
...contentProps
|
|
51
|
+
}: DialogFrameProps = $props();
|
|
52
|
+
</script>
|
|
53
|
+
|
|
54
|
+
<Dialog.Content
|
|
55
|
+
{...contentProps}
|
|
56
|
+
bind:ref
|
|
57
|
+
showCloseButton={false}
|
|
58
|
+
data-ui-dialog-frame=""
|
|
59
|
+
data-frame-size={size}
|
|
60
|
+
data-frame-surface={surface}
|
|
61
|
+
data-frame-padding={bodyPadding}
|
|
62
|
+
onInteractOutside={(event) => {
|
|
63
|
+
if (busy) event.preventDefault();
|
|
64
|
+
else onInteractOutside?.(event);
|
|
65
|
+
}}
|
|
66
|
+
onEscapeKeydown={(event) => {
|
|
67
|
+
if (busy) event.preventDefault();
|
|
68
|
+
else onEscapeKeydown?.(event);
|
|
69
|
+
}}
|
|
70
|
+
>
|
|
71
|
+
<div data-ui-part="dialog-frame-header">
|
|
72
|
+
<div data-ui-part="dialog-frame-heading">
|
|
73
|
+
<Dialog.Title
|
|
74
|
+
>{#if typeof title === "string"}{title}{:else}{@render title()}{/if}</Dialog.Title
|
|
75
|
+
>
|
|
76
|
+
{#if description}<Dialog.Description
|
|
77
|
+
class={descriptionHidden ? "sr-only" : undefined}
|
|
78
|
+
>{#if typeof description === "string"}{description}{:else}{@render description()}{/if}</Dialog.Description
|
|
79
|
+
>{/if}
|
|
80
|
+
</div>
|
|
81
|
+
{@render headerActions?.()}
|
|
82
|
+
<Dialog.Close>
|
|
83
|
+
{#snippet child({ props })}
|
|
84
|
+
<Button
|
|
85
|
+
{...props}
|
|
86
|
+
variant="ghost"
|
|
87
|
+
size="icon-sm"
|
|
88
|
+
disabled={busy}
|
|
89
|
+
aria-label={closeLabel}
|
|
90
|
+
data-ui-part="dialog-frame-close"><XIcon aria-hidden="true" /></Button
|
|
91
|
+
>
|
|
92
|
+
{/snippet}
|
|
93
|
+
</Dialog.Close>
|
|
94
|
+
</div>
|
|
95
|
+
<div data-ui-part="dialog-frame-layout" data-has-sidebar={Boolean(sidebar)}>
|
|
96
|
+
{#if sidebar}<aside data-ui-part="dialog-frame-sidebar">
|
|
97
|
+
{@render sidebar()}
|
|
98
|
+
</aside>{/if}
|
|
99
|
+
<div data-ui-part="dialog-frame-main">
|
|
100
|
+
<ScrollArea class="ui-dialog-frame-scroll" type="always">
|
|
101
|
+
<div data-ui-part="dialog-frame-body">{@render children()}</div>
|
|
102
|
+
</ScrollArea>
|
|
103
|
+
{#if footer}<Dialog.Footer data-ui-part="dialog-frame-footer"
|
|
104
|
+
>{@render footer()}</Dialog.Footer
|
|
105
|
+
>{/if}
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</Dialog.Content>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { DialogFrame } from "@lapismd/design-core/forms/dialog";
|
|
3
|
+
import * as Dialog from "@lapismd/design-core/shadcn/dialog";
|
|
4
|
+
import { Button } from "@lapismd/design-core/shadcn/button";
|
|
5
|
+
import { Input } from "@lapismd/design-core/shadcn/input";
|
|
6
|
+
let open = $state(false);
|
|
7
|
+
let name = $state("");
|
|
8
|
+
</script>
|
|
9
|
+
|
|
10
|
+
<Dialog.Root bind:open>
|
|
11
|
+
<Dialog.Trigger
|
|
12
|
+
>{#snippet child({ props })}<Button {...props}>Edit record</Button
|
|
13
|
+
>{/snippet}</Dialog.Trigger
|
|
14
|
+
>
|
|
15
|
+
<DialogFrame
|
|
16
|
+
title="Edit record"
|
|
17
|
+
description={name
|
|
18
|
+
? `Editing ${name}`
|
|
19
|
+
: "The form scrolls while its actions remain visible."}
|
|
20
|
+
size="large"
|
|
21
|
+
surface="inset"
|
|
22
|
+
class={name ? "record-filled" : "record-empty"}
|
|
23
|
+
>
|
|
24
|
+
{#snippet sidebar()}<nav aria-label="Sections">
|
|
25
|
+
<Button variant="secondary">Details</Button>
|
|
26
|
+
</nav>{/snippet}
|
|
27
|
+
{#each Array.from({ length: 24 }) as _, index}<label
|
|
28
|
+
style="display: grid; gap: 0.5rem; margin-block: 1rem;"
|
|
29
|
+
>Field {index + 1}{#if index === 0}<Input
|
|
30
|
+
bind:value={name}
|
|
31
|
+
/>{:else}<Input />{/if}</label
|
|
32
|
+
>{/each}
|
|
33
|
+
{#snippet footer()}<Button variant="outline" onclick={() => (open = false)}
|
|
34
|
+
>Cancel</Button
|
|
35
|
+
><Button onclick={() => (open = false)}>Save</Button>{/snippet}
|
|
36
|
+
</DialogFrame>
|
|
37
|
+
</Dialog.Root>
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { DialogFrame } from "./dialog-frame/index.js";
|
|
2
|
+
export type {
|
|
3
|
+
DialogFrameProps,
|
|
4
|
+
DialogFrameSize,
|
|
5
|
+
} from "./dialog-frame/index.js";
|
|
6
|
+
export { ConfirmDialog } from "./confirm-dialog/index.js";
|
|
7
|
+
export type { ConfirmDialogProps } from "./confirm-dialog/index.js";
|
|
8
|
+
export { ConfirmButton } from "./confirm-button/index.js";
|
|
@@ -129,3 +129,14 @@ export {
|
|
|
129
129
|
export type { InlineOptionPickerOption } from "./inline-option-picker/InlineOptionPicker.svelte";
|
|
130
130
|
export type { AddSectionOption } from "./add-section-chooser/AddSectionChooser.svelte";
|
|
131
131
|
export * from "./core/core";
|
|
132
|
+
|
|
133
|
+
export {
|
|
134
|
+
DialogFrame,
|
|
135
|
+
type DialogFrameProps,
|
|
136
|
+
type DialogFrameSize,
|
|
137
|
+
} from "./dialog-frame/index.js";
|
|
138
|
+
export {
|
|
139
|
+
ConfirmDialog,
|
|
140
|
+
type ConfirmDialogProps,
|
|
141
|
+
} from "./confirm-dialog/index.js";
|
|
142
|
+
export { ConfirmButton } from "./confirm-button/index.js";
|
|
@@ -276,6 +276,7 @@
|
|
|
276
276
|
}
|
|
277
277
|
[data-ui-component="button"][data-variant="outline"]:hover,
|
|
278
278
|
[data-ui-component="button"][data-variant="ghost"]:hover {
|
|
279
|
+
background-color: var(--accent);
|
|
279
280
|
color: var(--foreground);
|
|
280
281
|
}
|
|
281
282
|
[data-ui-component="button"][data-variant="link"]:hover {
|
|
@@ -294,6 +295,9 @@
|
|
|
294
295
|
);
|
|
295
296
|
}
|
|
296
297
|
}
|
|
298
|
+
[data-ui-component="button"][data-variant="ghost"]:focus-visible {
|
|
299
|
+
background-color: var(--accent);
|
|
300
|
+
}
|
|
297
301
|
[data-ui-component="button"]:focus-visible {
|
|
298
302
|
border-color: var(--ui-button-focus-ring-color, var(--ring));
|
|
299
303
|
}
|
|
@@ -179,8 +179,9 @@ function applyRangesToContainer(
|
|
|
179
179
|
for (let i = 0; i < lineDivs.length; i++) {
|
|
180
180
|
const tokenIndex = globalLineOffset + i;
|
|
181
181
|
const tokens = tokenLines[tokenIndex];
|
|
182
|
-
|
|
183
|
-
|
|
182
|
+
const lineDiv = lineDivs[i];
|
|
183
|
+
if (lineDiv && tokens && tokens.length > 0) {
|
|
184
|
+
applyLineRanges(lineDiv, tokens, results, resolve);
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
return results;
|
|
@@ -216,6 +217,7 @@ export function applyHighlightRangesChunked(
|
|
|
216
217
|
|
|
217
218
|
for (let i = 0; i < codeEl.children.length; i++) {
|
|
218
219
|
const child = codeEl.children[i];
|
|
220
|
+
if (!child) continue;
|
|
219
221
|
const lineDivs = child.querySelectorAll("[data-line]");
|
|
220
222
|
if (
|
|
221
223
|
lineDivs.length > 0 &&
|
|
@@ -235,8 +237,9 @@ export function applyHighlightRangesChunked(
|
|
|
235
237
|
const lineDivs = codeEl.querySelectorAll("[data-line]");
|
|
236
238
|
for (let i = 0; i < lineDivs.length; i++) {
|
|
237
239
|
const tokens = tokenLines[i];
|
|
238
|
-
|
|
239
|
-
|
|
240
|
+
const lineDiv = lineDivs[i];
|
|
241
|
+
if (lineDiv && tokens && tokens.length > 0) {
|
|
242
|
+
applyLineRanges(lineDiv, tokens, allRanges, resolve);
|
|
240
243
|
}
|
|
241
244
|
}
|
|
242
245
|
return () => cleanupRanges(allRanges);
|
|
@@ -253,7 +256,7 @@ export function applyHighlightRangesChunked(
|
|
|
253
256
|
const ranges = applyRangesToContainer(
|
|
254
257
|
wrapper,
|
|
255
258
|
tokenLines,
|
|
256
|
-
chunkLineOffsets[index],
|
|
259
|
+
chunkLineOffsets[index] ?? 0,
|
|
257
260
|
resolve,
|
|
258
261
|
);
|
|
259
262
|
chunkRanges.set(wrapper, ranges);
|
|
@@ -269,6 +272,7 @@ export function applyHighlightRangesChunked(
|
|
|
269
272
|
|
|
270
273
|
for (let i = 0; i < chunkWrappers.length; i++) {
|
|
271
274
|
const wrapper = chunkWrappers[i];
|
|
275
|
+
if (!wrapper) continue;
|
|
272
276
|
|
|
273
277
|
const handler = (e: Event) => {
|
|
274
278
|
// contentvisibilityautostatechange fires with .skipped = true when
|
|
@@ -347,7 +351,7 @@ export function applyHighlightRangesBatch(
|
|
|
347
351
|
|
|
348
352
|
const lineDiv = lineDivs[divIndex];
|
|
349
353
|
const tokens = tokenLines[i];
|
|
350
|
-
if (tokens && tokens.length > 0) {
|
|
354
|
+
if (lineDiv && tokens && tokens.length > 0) {
|
|
351
355
|
applyLineRanges(lineDiv, tokens, results, resolve);
|
|
352
356
|
}
|
|
353
357
|
}
|
|
@@ -60,11 +60,13 @@ export function resolveSyntaxTokenForMode(
|
|
|
60
60
|
mode: "light" | "dark",
|
|
61
61
|
): string {
|
|
62
62
|
if (Array.isArray(value)) {
|
|
63
|
-
return mode === "dark" ? value[1] : value[0];
|
|
63
|
+
return (mode === "dark" ? value[1] : value[0]) ?? "";
|
|
64
64
|
}
|
|
65
65
|
const match = value.match(/^light-dark\(([^,]+),([^)]+)\)$/);
|
|
66
66
|
if (match) {
|
|
67
|
-
return mode === "dark"
|
|
67
|
+
return mode === "dark"
|
|
68
|
+
? (match[2]?.trim() ?? "")
|
|
69
|
+
: (match[1]?.trim() ?? "");
|
|
68
70
|
}
|
|
69
71
|
return value;
|
|
70
72
|
}
|
|
@@ -517,13 +517,13 @@ export function flatTokensToLines(
|
|
|
517
517
|
for (const token of tokens) {
|
|
518
518
|
while (
|
|
519
519
|
lineIdx < lineStarts.length - 1 &&
|
|
520
|
-
token.start >= lineStarts[lineIdx + 1]
|
|
520
|
+
token.start >= (lineStarts[lineIdx + 1] ?? Number.POSITIVE_INFINITY)
|
|
521
521
|
) {
|
|
522
522
|
lineIdx++;
|
|
523
523
|
}
|
|
524
524
|
|
|
525
|
-
const lineStart = lineStarts[lineIdx];
|
|
526
|
-
result[lineIdx]
|
|
525
|
+
const lineStart = lineStarts[lineIdx] ?? 0;
|
|
526
|
+
result[lineIdx]?.push({
|
|
527
527
|
type: token.type,
|
|
528
528
|
start: token.start - lineStart,
|
|
529
529
|
end: token.end - lineStart,
|
|
@@ -19,12 +19,7 @@
|
|
|
19
19
|
data-slot="command-view"
|
|
20
20
|
class={className}
|
|
21
21
|
>
|
|
22
|
-
<Command.Root
|
|
23
|
-
bind:value
|
|
24
|
-
bind:ref
|
|
25
|
-
{shouldFilter}
|
|
26
|
-
{...restProps}
|
|
27
|
-
>
|
|
22
|
+
<Command.Root bind:value bind:ref {shouldFilter} {...restProps}>
|
|
28
23
|
{@render children?.()}
|
|
29
24
|
</Command.Root>
|
|
30
25
|
</div>
|