@lapismd/design-core 0.1.2 → 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.
Files changed (65) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE.md +3 -3
  3. package/README.md +20 -0
  4. package/package.json +24 -9
  5. package/spec/src/styling-and-themes.md +2 -0
  6. package/src/shared/ai/chat.css +23 -0
  7. package/src/shared/ai/chat.ts +12 -0
  8. package/src/shared/ai/context.svelte.ts +2 -0
  9. package/src/shared/ai/layout/Layout.svelte +116 -10
  10. package/src/shared/ai/message-list/MessageList.svelte +123 -43
  11. package/src/shared/ai/message-list/MessageListHistoryFixture.svelte +112 -0
  12. package/src/shared/ai/message-list/MessageListLegacyFixture.svelte +52 -0
  13. package/src/shared/ai/message-list/VirtualizedMessageRows.svelte +299 -0
  14. package/src/shared/ai/message-list/position.ts +7 -0
  15. package/src/shared/ai/new-messages.svelte.ts +24 -5
  16. package/src/shared/ai/stream-scroll.svelte.ts +135 -6
  17. package/src/shared/ai/tool-call-detail/ToolCallDetail.svelte +71 -0
  18. package/src/shared/ai/tool-call-detail/tool-call-detail.ts +209 -0
  19. package/src/shared/diff/core/merge/actions.ts +4 -3
  20. package/src/shared/diff/core/merge/render-model.ts +14 -10
  21. package/src/shared/diff/file-diff/FileDiff.css +68 -1
  22. package/src/shared/diff/file-diff/FileDiff.svelte +22 -6
  23. package/src/shared/diff/file-diff/FileDiffComposer.svelte +98 -16
  24. package/src/shared/diff/file-diff/index.ts +2 -0
  25. package/src/shared/diff/file-diff/types.ts +13 -0
  26. package/src/shared/diff/index.ts +2 -0
  27. package/src/shared/forms/confirm-button/ConfirmButton.svelte +38 -0
  28. package/src/shared/forms/confirm-button/ConfirmButtonExample.svelte +14 -0
  29. package/src/shared/forms/confirm-button/index.ts +1 -0
  30. package/src/shared/forms/confirm-dialog/ConfirmDialog.css +21 -0
  31. package/src/shared/forms/confirm-dialog/ConfirmDialog.svelte +116 -0
  32. package/src/shared/forms/confirm-dialog/ConfirmDialogExample.svelte +17 -0
  33. package/src/shared/forms/confirm-dialog/index.ts +2 -0
  34. package/src/shared/forms/dialog-frame/DialogFrame.css +130 -0
  35. package/src/shared/forms/dialog-frame/DialogFrame.svelte +108 -0
  36. package/src/shared/forms/dialog-frame/DialogFrameExample.svelte +37 -0
  37. package/src/shared/forms/dialog-frame/index.ts +6 -0
  38. package/src/shared/forms/dialog.ts +8 -0
  39. package/src/shared/forms/index.ts +11 -0
  40. package/src/shared/shadcn/button/button.svelte +4 -0
  41. package/src/shared/shadcn/code-block/highlightRanges.ts +10 -6
  42. package/src/shared/shadcn/code-block/syntax/defineSyntaxTheme.ts +4 -2
  43. package/src/shared/shadcn/code-block/tokenizer.ts +3 -3
  44. package/src/shared/shadcn/dialog/dialog-content.svelte +3 -4
  45. package/src/shared/shadcn/dialog/dialog-portal.svelte +10 -2
  46. package/src/shared/shadcn/tooltip/tooltip-content.svelte +10 -4
  47. package/src/shared/shadcn/tooltip/tooltip.tokens.ts +4 -0
  48. package/src/shared/shell/app-shell/AppShell.css +112 -32
  49. package/src/shared/shell/app-shell/AppShellMain.svelte +12 -1
  50. package/src/shared/shell/app-shell/AppShellRoot.svelte +27 -13
  51. package/src/shared/shell/app-shell/AppShellSidebar.svelte +121 -19
  52. package/src/shared/shell/app-shell/AppShellSidebarClose.svelte +17 -17
  53. package/src/shared/shell/app-shell/AppShellSidebarToggle.svelte +38 -58
  54. package/src/shared/shell/app-shell/AppShellSurfaceLayer.svelte +173 -0
  55. package/src/shared/shell/app-shell/AppShellSurfaceLayerBody.svelte +32 -0
  56. package/src/shared/shell/app-shell/AppShellSurfaceLayerHeader.svelte +26 -0
  57. package/src/shared/shell/app-shell/app-shell-context.svelte.ts +6 -3
  58. package/src/shared/shell/app-shell/app-shell-controller.svelte.ts +246 -2
  59. package/src/shared/shell/app-shell/app-shell-sidebar-context.svelte.ts +1 -0
  60. package/src/shared/shell/app-shell/examples/AppShellExamples.css +103 -0
  61. package/src/shared/shell/app-shell/examples/AppShellMultiPanelDemo.svelte +187 -0
  62. package/src/shared/shell/app-shell/index.ts +14 -0
  63. package/src/shared/shell/shell.tokens.css +2 -0
  64. package/src/shared/shell/shell.tokens.ts +5 -0
  65. package/src/theme.css +10 -0
@@ -40,6 +40,7 @@
40
40
  language,
41
41
  scrollTo,
42
42
  lineAccessory,
43
+ lineAnnotation,
43
44
  }: {
44
45
  path: string;
45
46
  oldText?: string | null;
@@ -51,6 +52,8 @@
51
52
  language?: string;
52
53
  scrollTo?: FileDiffScrollTarget | null;
53
54
  lineAccessory?: Snippet<[FileDiffLineContext]>;
55
+ /** Host-owned content inserted after each matching unified diff row. */
56
+ lineAnnotation?: Snippet<[FileDiffLineContext]>;
54
57
  } = $props();
55
58
 
56
59
  let expandedBlocks = $state<ExpandedBlockState>({});
@@ -197,6 +200,14 @@
197
200
  data-diff-line-variant={row.variant}
198
201
  data-variant={row.variant}
199
202
  >
203
+ {#if lineAccessory}
204
+ <div
205
+ class="ui-diff-file-diff__accessory"
206
+ data-ui-part="diff-row-accessory"
207
+ >
208
+ {@render lineAccessory(lineContext(row))}
209
+ </div>
210
+ {/if}
200
211
  <span class="ui-diff-file-diff__gutter" aria-hidden="true">
201
212
  {row.lineNumber ?? ""}
202
213
  </span>
@@ -204,10 +215,10 @@
204
215
  {row.variant === "added" ? "+" : row.variant === "removed" ? "-" : " "}
205
216
  </span>
206
217
  {@render highlightedText(row)}
207
- {#if lineAccessory}
208
- {@render lineAccessory(lineContext(row))}
209
- {/if}
210
218
  </div>
219
+ {#if lineAnnotation}
220
+ {@render lineAnnotation(lineContext(row))}
221
+ {/if}
211
222
  {/snippet}
212
223
 
213
224
  {#snippet splitCell(
@@ -226,13 +237,18 @@
226
237
  data-side={side}
227
238
  data-pair-key={pairKey}
228
239
  >
240
+ {#if lineAccessory}
241
+ <div
242
+ class="ui-diff-file-diff__accessory"
243
+ data-ui-part="diff-row-accessory"
244
+ >
245
+ {@render lineAccessory(lineContext(row))}
246
+ </div>
247
+ {/if}
229
248
  <span class="ui-diff-file-diff__gutter" aria-hidden="true">
230
249
  {lineNumberForSplitSide(row, side) ?? ""}
231
250
  </span>
232
251
  {@render highlightedText(row)}
233
- {#if lineAccessory}
234
- {@render lineAccessory(lineContext(row))}
235
- {/if}
236
252
  </div>
237
253
  {:else}
238
254
  <div
@@ -1,9 +1,15 @@
1
1
  <script lang="ts">
2
2
  import type { Snippet } from "svelte";
3
+ import { tick } from "svelte";
4
+ import ChevronDownIcon from "@lucide/svelte/icons/chevron-down";
5
+ import ChevronRightIcon from "@lucide/svelte/icons/chevron-right";
6
+ import { Button } from "../../shadcn/button/index.js";
3
7
  import { truncatePathMiddle } from "../core/unified-diff.js";
4
8
  import FileDiff from "./FileDiff.svelte";
5
9
  import type {
10
+ FileDiffComposerFileContext,
6
11
  FileDiffFile,
12
+ FileDiffFileScrollTarget,
7
13
  FileDiffLineContext,
8
14
  FileDiffScrollTarget,
9
15
  FileDiffViewMode,
@@ -13,48 +19,124 @@
13
19
  let {
14
20
  files,
15
21
  viewMode = "unified",
22
+ selectedPath,
23
+ collapsedFilePaths = [],
24
+ scrollToFile,
25
+ stickyHeaders = false,
16
26
  scrollTo,
27
+ onFileToggle,
28
+ fileHeaderLeading,
29
+ fileHeaderTrailing,
17
30
  lineAccessory,
31
+ lineAnnotation,
18
32
  }: {
19
33
  files: FileDiffFile[];
20
34
  viewMode?: FileDiffViewMode;
35
+ selectedPath?: string;
36
+ collapsedFilePaths?: readonly string[];
37
+ scrollToFile?: FileDiffFileScrollTarget | null;
38
+ stickyHeaders?: boolean;
21
39
  scrollTo?: FileDiffScrollTarget | null;
40
+ onFileToggle?: (path: string, collapsed: boolean) => void;
41
+ fileHeaderLeading?: Snippet<[FileDiffComposerFileContext]>;
42
+ fileHeaderTrailing?: Snippet<[FileDiffComposerFileContext]>;
22
43
  lineAccessory?: Snippet<[FileDiffLineContext]>;
44
+ /** Host-owned content inserted after each matching unified diff row. */
45
+ lineAnnotation?: Snippet<[FileDiffLineContext]>;
23
46
  } = $props();
47
+
48
+ let rootEl: HTMLElement | null = $state(null);
49
+
50
+ function fileContext(file: FileDiffFile): FileDiffComposerFileContext {
51
+ return {
52
+ file,
53
+ path: file.path,
54
+ collapsed: collapsedFilePaths.includes(file.path),
55
+ selected: selectedPath === file.path,
56
+ };
57
+ }
58
+
59
+ $effect(() => {
60
+ const target = scrollToFile;
61
+ if (!target || !rootEl) return;
62
+ target.requestId;
63
+ void tick().then(() => {
64
+ const section = [
65
+ ...(rootEl?.querySelectorAll<HTMLElement>("[data-diff-file-path]") ??
66
+ []),
67
+ ].find((candidate) => candidate.dataset.diffFilePath === target.path);
68
+ section?.scrollIntoView({ block: "start", inline: "nearest" });
69
+ });
70
+ });
24
71
  </script>
25
72
 
26
73
  <div
74
+ bind:this={rootEl}
27
75
  class="ui-diff-file-diff-composer"
28
76
  data-ui-component="file-diff-composer"
29
77
  data-ui-part="file-diff-composer"
78
+ data-sticky-headers={stickyHeaders}
30
79
  >
31
80
  {#if files.length === 0}
32
81
  <p class="ui-diff-file-diff__empty">No file diffs</p>
33
82
  {:else}
34
83
  {#each files as file (file.path)}
84
+ {@const context = fileContext(file)}
35
85
  <section
36
86
  class="ui-diff-file-diff-composer__file"
37
87
  data-ui-part="file-section"
38
88
  data-diff-file-path={file.path}
89
+ data-selected={context.selected}
90
+ data-collapsed={context.collapsed}
39
91
  >
40
92
  <header class="ui-diff-file-diff-composer__header">
41
- <span title={file.path}>{truncatePathMiddle(file.path)}</span>
93
+ <div class="ui-diff-file-diff-composer__header-start">
94
+ {#if fileHeaderLeading}
95
+ <div class="ui-diff-file-diff-composer__header-leading">
96
+ {@render fileHeaderLeading(context)}
97
+ </div>
98
+ {/if}
99
+ {#if onFileToggle}
100
+ <Button
101
+ type="button"
102
+ variant="ghost"
103
+ size="icon-xs"
104
+ aria-label={`${context.collapsed ? "Expand" : "Collapse"} ${file.path}`}
105
+ onclick={() => onFileToggle?.(file.path, !context.collapsed)}
106
+ >
107
+ {#if context.collapsed}
108
+ <ChevronRightIcon aria-hidden="true" />
109
+ {:else}
110
+ <ChevronDownIcon aria-hidden="true" />
111
+ {/if}
112
+ </Button>
113
+ {/if}
114
+ <span title={file.path}>{truncatePathMiddle(file.path)}</span>
115
+ </div>
116
+ {#if fileHeaderTrailing}
117
+ <div class="ui-diff-file-diff-composer__header-trailing">
118
+ {@render fileHeaderTrailing(context)}
119
+ </div>
120
+ {/if}
42
121
  </header>
43
- <FileDiff
44
- path={file.path}
45
- oldText={file.oldText}
46
- newText={file.newText}
47
- patch={file.patch}
48
- language={file.language}
49
- {viewMode}
50
- scrollTo={scrollTo?.path === file.path ||
51
- (scrollTo != null &&
52
- scrollTo.path == null &&
53
- files[0]?.path === file.path)
54
- ? scrollTo
55
- : null}
56
- {lineAccessory}
57
- />
122
+ {#if !context.collapsed}
123
+ <FileDiff
124
+ path={file.path}
125
+ oldText={file.oldText}
126
+ newText={file.newText}
127
+ patch={file.patch}
128
+ language={file.language}
129
+ {viewMode}
130
+ scrollTo={scrollTo?.path === file.path ||
131
+ (scrollTo != null &&
132
+ scrollTo.path == null &&
133
+ files[0]?.path === file.path)
134
+ ? scrollTo
135
+ : null}
136
+ {lineAccessory}
137
+ {lineAnnotation}
138
+ />
139
+ {/if}
58
140
  </section>
59
141
  {/each}
60
142
  {/if}
@@ -2,7 +2,9 @@ export { default as FileDiff } from "./FileDiff.svelte";
2
2
  export { default as FileDiffComposer } from "./FileDiffComposer.svelte";
3
3
  export { resolveDiffLanguage } from "./language.js";
4
4
  export type {
5
+ FileDiffComposerFileContext,
5
6
  FileDiffFile,
7
+ FileDiffFileScrollTarget,
6
8
  FileDiffLineContext,
7
9
  FileDiffScrollTarget,
8
10
  FileDiffViewMode,
@@ -17,6 +17,19 @@ export interface FileDiffFile {
17
17
  language?: string;
18
18
  }
19
19
 
20
+ export interface FileDiffComposerFileContext {
21
+ file: FileDiffFile;
22
+ path: string;
23
+ collapsed: boolean;
24
+ selected: boolean;
25
+ }
26
+
27
+ export interface FileDiffFileScrollTarget {
28
+ path: string;
29
+ /** Changes for every scroll request, including repeated requests for one path. */
30
+ requestId: string | number;
31
+ }
32
+
20
33
  export interface FileDiffScrollTarget {
21
34
  path?: string;
22
35
  lineNumber: number;
@@ -14,7 +14,9 @@ export {
14
14
  FileDiff,
15
15
  FileDiffComposer,
16
16
  resolveDiffLanguage,
17
+ type FileDiffComposerFileContext,
17
18
  type FileDiffFile,
19
+ type FileDiffFileScrollTarget,
18
20
  type FileDiffLineContext,
19
21
  type FileDiffScrollTarget,
20
22
  type FileDiffViewMode,
@@ -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,2 @@
1
+ export { default as ConfirmDialog } from "./ConfirmDialog.svelte";
2
+ export type { ConfirmDialogProps } from "./ConfirmDialog.svelte";
@@ -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
+ }