@plures/design-dojo 0.5.2 → 0.7.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/dist/app/CanvasBreadcrumb.svelte +146 -0
- package/dist/app/CanvasBreadcrumb.svelte.d.ts +19 -0
- package/dist/app/CanvasBreadcrumb.types.js +1 -0
- package/dist/app/ChatInput.svelte +296 -0
- package/dist/app/ChatInput.svelte.d.ts +15 -0
- package/dist/app/ChatView.svelte +542 -0
- package/dist/app/ChatView.svelte.d.ts +19 -0
- package/dist/app/ChatView.types.js +1 -0
- package/dist/app/ConversationGraph.svelte +471 -0
- package/dist/app/ConversationGraph.svelte.d.ts +20 -0
- package/dist/app/FirstRunWizard.svelte +542 -0
- package/dist/app/FirstRunWizard.svelte.d.ts +10 -0
- package/dist/app/FirstRunWizard.types.js +1 -0
- package/dist/app/MemorySidebar.svelte +258 -0
- package/dist/app/MemorySidebar.svelte.d.ts +9 -0
- package/dist/app/MemorySidebar.types.js +1 -0
- package/dist/app/PeerStatusPanel.svelte +464 -0
- package/dist/app/PeerStatusPanel.svelte.d.ts +16 -0
- package/dist/app/ProcedureCanvas.svelte +994 -0
- package/dist/app/ProcedureCanvas.svelte.d.ts +12 -0
- package/dist/app/ProcedureCanvas.types.js +1 -0
- package/dist/app/ProcedureEditor.svelte +494 -0
- package/dist/app/ProcedureEditor.svelte.d.ts +11 -0
- package/dist/app/ProcedureEditor.types.js +1 -0
- package/dist/app/ProcedureInspector.svelte +520 -0
- package/dist/app/ProcedureInspector.svelte.d.ts +15 -0
- package/dist/app/ProcedureNode.svelte +283 -0
- package/dist/app/ProcedureNode.svelte.d.ts +26 -0
- package/dist/app/Realm.types.js +1 -0
- package/dist/app/RealmIndicator.svelte +81 -0
- package/dist/app/RealmIndicator.svelte.d.ts +10 -0
- package/dist/app/RealmSwitcher.svelte +354 -0
- package/dist/app/RealmSwitcher.svelte.d.ts +16 -0
- package/dist/app/SemanticConversation.types.js +1 -0
- package/dist/app/SemanticSearchInput.svelte +630 -0
- package/dist/app/SemanticSearchInput.svelte.d.ts +20 -0
- package/dist/app/SemanticSearchInput.types.js +1 -0
- package/dist/app/SemanticTimeline.svelte +426 -0
- package/dist/app/SemanticTimeline.svelte.d.ts +13 -0
- package/dist/app/SettingsPanel.svelte +330 -0
- package/dist/app/SettingsPanel.svelte.d.ts +12 -0
- package/dist/app/SettingsPanel.types.js +1 -0
- package/dist/app/SubCanvas.svelte +457 -0
- package/dist/app/SubCanvas.svelte.d.ts +48 -0
- package/dist/app/SubCanvas.types.js +1 -0
- package/dist/app/Sync.types.js +1 -0
- package/dist/app/SyncIndicator.svelte +219 -0
- package/dist/app/SyncIndicator.svelte.d.ts +15 -0
- package/dist/app/SyncTimeline.svelte +299 -0
- package/dist/app/SyncTimeline.svelte.d.ts +12 -0
- package/dist/app/TagCloud.svelte +287 -0
- package/dist/app/TagCloud.svelte.d.ts +12 -0
- package/dist/app/WorkModeToggle.svelte +188 -0
- package/dist/app/WorkModeToggle.svelte.d.ts +17 -0
- package/dist/data/List.svelte +104 -0
- package/dist/data/List.svelte.d.ts +18 -0
- package/dist/data/ListItem.svelte +130 -0
- package/dist/data/ListItem.svelte.d.ts +12 -0
- package/dist/data/Table.svelte +241 -0
- package/dist/data/Table.svelte.d.ts +17 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/disclosure/Accordion.svelte +48 -0
- package/dist/disclosure/Accordion.svelte.d.ts +15 -0
- package/dist/feedback/Badge.svelte +60 -0
- package/dist/feedback/Badge.svelte.d.ts +14 -0
- package/dist/feedback/Callout.svelte +52 -0
- package/dist/feedback/Callout.svelte.d.ts +12 -0
- package/dist/feedback/EmptyState.svelte +47 -0
- package/dist/feedback/EmptyState.svelte.d.ts +12 -0
- package/dist/feedback/ProgressBar.svelte +95 -0
- package/dist/feedback/ProgressBar.svelte.d.ts +16 -0
- package/dist/forms/FileUpload.svelte +99 -0
- package/dist/forms/FileUpload.svelte.d.ts +18 -0
- package/dist/forms/RadioGroup.svelte +84 -0
- package/dist/forms/RadioGroup.svelte.d.ts +19 -0
- package/dist/icons/NerdFont.svelte +44 -0
- package/dist/icons/NerdFont.svelte.d.ts +13 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +70 -6212
- package/dist/layout/Box.svelte +207 -0
- package/dist/layout/Box.svelte.d.ts +22 -0
- package/dist/layout/Sidebar.svelte +210 -0
- package/dist/layout/Sidebar.svelte.d.ts +22 -0
- package/dist/layout/SplitPane.svelte +64 -0
- package/dist/layout/SplitPane.svelte.d.ts +12 -0
- package/dist/layout/StatusBar.svelte +83 -0
- package/dist/layout/StatusBar.svelte.d.ts +12 -0
- package/dist/layout/StatusBarItem.svelte +146 -0
- package/dist/layout/StatusBarItem.svelte.d.ts +15 -0
- package/dist/layout/StatusBarSpacer.svelte +38 -0
- package/dist/layout/StatusBarSpacer.svelte.d.ts +3 -0
- package/dist/layout/Tabs.svelte +254 -0
- package/dist/layout/Tabs.svelte.d.ts +21 -0
- package/dist/layout/Tabs.types.js +1 -0
- package/dist/layout/TitleBar.svelte +422 -0
- package/dist/layout/TitleBar.svelte.d.ts +22 -0
- package/dist/layout/index.d.ts +9 -0
- package/dist/layout/index.js +8 -0
- package/dist/motion/index.d.ts +1 -0
- package/dist/motion/index.js +1 -0
- package/dist/motion/spring.js +116 -0
- package/dist/overlays/ContextMenu.svelte +268 -0
- package/dist/overlays/ContextMenu.svelte.d.ts +17 -0
- package/dist/overlays/Dialog.svelte +264 -0
- package/dist/overlays/Dialog.svelte.d.ts +20 -0
- package/dist/overlays/Menu.svelte +274 -0
- package/dist/overlays/Menu.svelte.d.ts +26 -0
- package/dist/overlays/Menu.types.js +1 -0
- package/dist/overlays/Popover.svelte +158 -0
- package/dist/overlays/Popover.svelte.d.ts +21 -0
- package/dist/overlays/Toast.svelte +179 -0
- package/dist/overlays/Toast.svelte.d.ts +19 -0
- package/dist/overlays/Tooltip.svelte +114 -0
- package/dist/overlays/Tooltip.svelte.d.ts +17 -0
- package/dist/overlays/index.d.ts +7 -0
- package/dist/overlays/index.js +6 -0
- package/dist/primitives/Button.svelte +217 -0
- package/dist/primitives/Button.svelte.d.ts +13 -0
- package/dist/primitives/ContextMenu.svelte +242 -0
- package/dist/primitives/ContextMenu.svelte.d.ts +18 -0
- package/dist/primitives/ContextMenu.types.js +1 -0
- package/dist/primitives/Input.svelte +468 -0
- package/dist/primitives/Input.svelte.d.ts +21 -0
- package/dist/primitives/MarkdownEditor.svelte +781 -0
- package/dist/primitives/MarkdownEditor.svelte.d.ts +21 -0
- package/dist/primitives/MarkdownEditor.types.js +1 -0
- package/dist/primitives/SearchInput.svelte +623 -0
- package/dist/primitives/SearchInput.svelte.d.ts +24 -0
- package/dist/primitives/Select.svelte +336 -0
- package/dist/primitives/Select.svelte.d.ts +18 -0
- package/dist/primitives/Text.svelte +177 -0
- package/dist/primitives/Text.svelte.d.ts +26 -0
- package/dist/primitives/Toggle.svelte +138 -0
- package/dist/primitives/Toggle.svelte.d.ts +9 -0
- package/dist/primitives/index.d.ts +9 -0
- package/dist/primitives/index.js +7 -0
- package/dist/primitives/search-input-types.js +1 -0
- package/dist/surfaces/ChatPane.svelte +520 -0
- package/dist/surfaces/ChatPane.svelte.d.ts +15 -0
- package/dist/surfaces/ChatPane.types.js +1 -0
- package/dist/surfaces/GlassPanel.svelte +118 -0
- package/dist/surfaces/GlassPanel.svelte.d.ts +19 -0
- package/dist/surfaces/Pane.svelte +172 -0
- package/dist/surfaces/Pane.svelte.d.ts +25 -0
- package/dist/surfaces/index.d.ts +4 -0
- package/dist/surfaces/index.js +3 -0
- package/dist/telemetry/correlation.js +26 -0
- package/dist/telemetry/index.d.ts +4 -4
- package/dist/telemetry/index.js +20 -101
- package/dist/telemetry/sampling.js +58 -0
- package/dist/telemetry/tracer.d.ts +16 -1
- package/dist/telemetry/tracer.js +112 -0
- package/dist/tokens.css +123 -0
- package/dist/tui-tokens.css +36 -0
- package/dist/useTui.js +31 -0
- package/package.json +32 -22
- package/dist/design-dojo.css +0 -1
- package/dist/enforce/index.d.ts +0 -75
- package/dist/enforce/known-components.d.ts +0 -7
- package/dist/enforce/rules/no-local-components.d.ts +0 -29
- package/dist/enforce/rules/prefer-design-dojo-imports.d.ts +0 -27
- package/dist/enforce.js +0 -132
- package/dist/lib/app/CanvasBreadcrumb.svelte.d.ts +0 -1
- package/dist/lib/app/ChatInput.svelte.d.ts +0 -1
- package/dist/lib/app/ChatView.svelte.d.ts +0 -1
- package/dist/lib/app/ConversationGraph.svelte.d.ts +0 -1
- package/dist/lib/app/FirstRunWizard.svelte.d.ts +0 -1
- package/dist/lib/app/MemorySidebar.svelte.d.ts +0 -1
- package/dist/lib/app/PeerStatusPanel.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureCanvas.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureEditor.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureInspector.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureNode.svelte.d.ts +0 -1
- package/dist/lib/app/RealmIndicator.svelte.d.ts +0 -1
- package/dist/lib/app/RealmSwitcher.svelte.d.ts +0 -1
- package/dist/lib/app/SemanticSearchInput.svelte.d.ts +0 -1
- package/dist/lib/app/SemanticTimeline.svelte.d.ts +0 -1
- package/dist/lib/app/SettingsPanel.svelte.d.ts +0 -1
- package/dist/lib/app/SubCanvas.svelte.d.ts +0 -1
- package/dist/lib/app/SyncIndicator.svelte.d.ts +0 -1
- package/dist/lib/app/SyncTimeline.svelte.d.ts +0 -1
- package/dist/lib/app/TagCloud.svelte.d.ts +0 -1
- package/dist/lib/app/WorkModeToggle.svelte.d.ts +0 -1
- package/dist/lib/data/List.svelte.d.ts +0 -1
- package/dist/lib/data/ListItem.svelte.d.ts +0 -1
- package/dist/lib/data/Table.svelte.d.ts +0 -1
- package/dist/lib/data/index.d.ts +0 -3
- package/dist/lib/disclosure/Accordion.svelte.d.ts +0 -1
- package/dist/lib/feedback/Badge.svelte.d.ts +0 -1
- package/dist/lib/feedback/Callout.svelte.d.ts +0 -1
- package/dist/lib/feedback/EmptyState.svelte.d.ts +0 -1
- package/dist/lib/feedback/ProgressBar.svelte.d.ts +0 -1
- package/dist/lib/forms/FileUpload.svelte.d.ts +0 -1
- package/dist/lib/forms/RadioGroup.svelte.d.ts +0 -1
- package/dist/lib/icons/NerdFont.svelte.d.ts +0 -1
- package/dist/lib/icons/index.d.ts +0 -1
- package/dist/lib/index.d.ts +0 -76
- package/dist/lib/layout/Box.svelte.d.ts +0 -1
- package/dist/lib/layout/Sidebar.svelte.d.ts +0 -1
- package/dist/lib/layout/SplitPane.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBar.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBarItem.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBarSpacer.svelte.d.ts +0 -1
- package/dist/lib/layout/Tabs.svelte.d.ts +0 -1
- package/dist/lib/layout/TitleBar.svelte.d.ts +0 -1
- package/dist/lib/layout/index.d.ts +0 -9
- package/dist/lib/motion/index.d.ts +0 -1
- package/dist/lib/overlays/ContextMenu.svelte.d.ts +0 -1
- package/dist/lib/overlays/Dialog.svelte.d.ts +0 -1
- package/dist/lib/overlays/Menu.svelte.d.ts +0 -1
- package/dist/lib/overlays/Popover.svelte.d.ts +0 -1
- package/dist/lib/overlays/Toast.svelte.d.ts +0 -1
- package/dist/lib/overlays/Tooltip.svelte.d.ts +0 -1
- package/dist/lib/overlays/index.d.ts +0 -7
- package/dist/lib/primitives/Button.svelte.d.ts +0 -1
- package/dist/lib/primitives/ContextMenu.svelte.d.ts +0 -1
- package/dist/lib/primitives/Input.svelte.d.ts +0 -1
- package/dist/lib/primitives/MarkdownEditor.svelte.d.ts +0 -1
- package/dist/lib/primitives/SearchInput.svelte.d.ts +0 -1
- package/dist/lib/primitives/Select.svelte.d.ts +0 -1
- package/dist/lib/primitives/Text.svelte.d.ts +0 -1
- package/dist/lib/primitives/Toggle.svelte.d.ts +0 -1
- package/dist/lib/primitives/index.d.ts +0 -9
- package/dist/lib/surfaces/ChatPane.svelte.d.ts +0 -1
- package/dist/lib/surfaces/GlassPanel.svelte.d.ts +0 -1
- package/dist/lib/surfaces/Pane.svelte.d.ts +0 -1
- package/dist/lib/surfaces/index.d.ts +0 -4
- /package/dist/{lib/app → app}/CanvasBreadcrumb.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ChatView.types.d.ts +0 -0
- /package/dist/{lib/app → app}/FirstRunWizard.types.d.ts +0 -0
- /package/dist/{lib/app → app}/MemorySidebar.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ProcedureCanvas.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ProcedureEditor.types.d.ts +0 -0
- /package/dist/{lib/app → app}/Realm.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SemanticConversation.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SemanticSearchInput.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SettingsPanel.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SubCanvas.types.d.ts +0 -0
- /package/dist/{lib/app → app}/Sync.types.d.ts +0 -0
- /package/dist/{lib/layout → layout}/Tabs.types.d.ts +0 -0
- /package/dist/{lib/motion → motion}/spring.d.ts +0 -0
- /package/dist/{lib/overlays → overlays}/Menu.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/ContextMenu.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/MarkdownEditor.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/search-input-types.d.ts +0 -0
- /package/dist/{lib/surfaces → surfaces}/ChatPane.types.d.ts +0 -0
- /package/dist/{lib/useTui.d.ts → useTui.d.ts} +0 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Menu — Dropdown list of labeled actions, opened by a trigger.
|
|
3
|
+
|
|
4
|
+
GUI mode: floating panel with keyboard navigation.
|
|
5
|
+
TUI mode: box-drawing bordered list rendered inline.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { useTui } from "../useTui.js";
|
|
9
|
+
import type { Snippet } from "svelte";
|
|
10
|
+
import type { MenuItem } from "./Menu.types.js";
|
|
11
|
+
|
|
12
|
+
export type { MenuItem };
|
|
13
|
+
|
|
14
|
+
interface Props {
|
|
15
|
+
/** Menu items. */
|
|
16
|
+
items: MenuItem[];
|
|
17
|
+
/** Control open state externally. */
|
|
18
|
+
open?: boolean;
|
|
19
|
+
/** Called when an item is selected. */
|
|
20
|
+
onselect?: (key: string) => void;
|
|
21
|
+
/** Called when the menu requests to close. */
|
|
22
|
+
onclose?: () => void;
|
|
23
|
+
/** Preferred opening position. Default: "bottom-start". */
|
|
24
|
+
position?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
|
|
25
|
+
/** Enable TUI mode. */
|
|
26
|
+
tui?: boolean;
|
|
27
|
+
/** Trigger element. */
|
|
28
|
+
trigger: Snippet<[{ toggle: () => void; isOpen: boolean }]>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let {
|
|
32
|
+
items,
|
|
33
|
+
open = $bindable(false),
|
|
34
|
+
onselect,
|
|
35
|
+
onclose,
|
|
36
|
+
position = "bottom-start",
|
|
37
|
+
tui = false,
|
|
38
|
+
trigger,
|
|
39
|
+
}: Props = $props();
|
|
40
|
+
|
|
41
|
+
const getTuiCtx = useTui();
|
|
42
|
+
const isTui = $derived(tui || getTuiCtx());
|
|
43
|
+
|
|
44
|
+
function createId(prefix: string) {
|
|
45
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
46
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
47
|
+
}
|
|
48
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
49
|
+
const array = new Uint32Array(1);
|
|
50
|
+
crypto.getRandomValues(array);
|
|
51
|
+
return `${prefix}-${array[0].toString(36)}`;
|
|
52
|
+
}
|
|
53
|
+
return `${prefix}-${Date.now().toString(36)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const menuId = createId("menu");
|
|
57
|
+
|
|
58
|
+
// Navigable items: exclude both disabled and separator entries
|
|
59
|
+
const navigable = $derived(items.filter((i) => !i.disabled && !i.separator));
|
|
60
|
+
|
|
61
|
+
// Track the focused item by key
|
|
62
|
+
let focusedKey = $state<string>("");
|
|
63
|
+
|
|
64
|
+
function toggle() {
|
|
65
|
+
open = !open;
|
|
66
|
+
if (open) focusedKey = navigable[0]?.key ?? "";
|
|
67
|
+
}
|
|
68
|
+
function close() { open = false; onclose?.(); }
|
|
69
|
+
|
|
70
|
+
function select(key: string) {
|
|
71
|
+
onselect?.(key);
|
|
72
|
+
close();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
76
|
+
if (!open) return;
|
|
77
|
+
const cur = navigable.findIndex((i) => i.key === focusedKey);
|
|
78
|
+
if (e.key === "Escape") { e.preventDefault(); close(); }
|
|
79
|
+
else if (e.key === "ArrowDown") {
|
|
80
|
+
e.preventDefault();
|
|
81
|
+
const next = navigable[Math.min(cur + 1, navigable.length - 1)];
|
|
82
|
+
if (next) focusedKey = next.key;
|
|
83
|
+
} else if (e.key === "ArrowUp") {
|
|
84
|
+
e.preventDefault();
|
|
85
|
+
const prev = navigable[Math.max(cur - 1, 0)];
|
|
86
|
+
if (prev) focusedKey = prev.key;
|
|
87
|
+
} else if (e.key === "Enter" || e.key === " ") {
|
|
88
|
+
e.preventDefault();
|
|
89
|
+
const item = navigable[cur];
|
|
90
|
+
if (item) select(item.key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
96
|
+
<div class="menu-host" class:tui={isTui} onkeydown={handleKeydown}>
|
|
97
|
+
{@render trigger({ toggle, isOpen: open })}
|
|
98
|
+
|
|
99
|
+
{#if open}
|
|
100
|
+
{#if isTui}
|
|
101
|
+
<ul class="tui-menu" role="menu" id={menuId}>
|
|
102
|
+
{#each items as item}
|
|
103
|
+
{#if item.separator}
|
|
104
|
+
<li class="tui-separator" role="separator">├{'─'.repeat(12)}┤</li>
|
|
105
|
+
{:else}
|
|
106
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
107
|
+
<li
|
|
108
|
+
class="tui-item"
|
|
109
|
+
class:focused={item.key === focusedKey}
|
|
110
|
+
class:disabled={item.disabled}
|
|
111
|
+
role="menuitem"
|
|
112
|
+
aria-disabled={item.disabled}
|
|
113
|
+
tabindex={item.disabled ? -1 : 0}
|
|
114
|
+
onclick={() => !item.disabled && select(item.key)}
|
|
115
|
+
onkeydown={(e) => {
|
|
116
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
117
|
+
e.preventDefault();
|
|
118
|
+
if (!item.disabled) select(item.key);
|
|
119
|
+
}
|
|
120
|
+
}}
|
|
121
|
+
>
|
|
122
|
+
{#if item.icon}<span class="tui-icon" aria-hidden="true">{item.icon}</span>{/if}
|
|
123
|
+
<span class="tui-label">{item.label}</span>
|
|
124
|
+
{#if item.shortcut}<span class="tui-shortcut">{item.shortcut}</span>{/if}
|
|
125
|
+
</li>
|
|
126
|
+
{/if}
|
|
127
|
+
{/each}
|
|
128
|
+
</ul>
|
|
129
|
+
{:else}
|
|
130
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
131
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
132
|
+
<div class="menu-backdrop" onclick={close} aria-hidden="true"></div>
|
|
133
|
+
<ul class="menu menu--{position}" role="menu" id={menuId}>
|
|
134
|
+
{#each items as item}
|
|
135
|
+
{#if item.separator}
|
|
136
|
+
<li class="menu-separator" role="separator"></li>
|
|
137
|
+
{:else}
|
|
138
|
+
<li
|
|
139
|
+
class="menu-item"
|
|
140
|
+
class:focused={item.key === focusedKey}
|
|
141
|
+
class:disabled={item.disabled}
|
|
142
|
+
role="menuitem"
|
|
143
|
+
aria-disabled={item.disabled}
|
|
144
|
+
tabindex={item.disabled ? -1 : 0}
|
|
145
|
+
onclick={() => !item.disabled && select(item.key)}
|
|
146
|
+
onkeydown={(e) => {
|
|
147
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
148
|
+
e.preventDefault();
|
|
149
|
+
if (!item.disabled) select(item.key);
|
|
150
|
+
}
|
|
151
|
+
}}
|
|
152
|
+
>
|
|
153
|
+
{#if item.icon}<span class="menu-icon" aria-hidden="true">{item.icon}</span>{/if}
|
|
154
|
+
<span class="menu-label">{item.label}</span>
|
|
155
|
+
{#if item.shortcut}<span class="menu-shortcut">{item.shortcut}</span>{/if}
|
|
156
|
+
</li>
|
|
157
|
+
{/if}
|
|
158
|
+
{/each}
|
|
159
|
+
</ul>
|
|
160
|
+
{/if}
|
|
161
|
+
{/if}
|
|
162
|
+
</div>
|
|
163
|
+
|
|
164
|
+
<style>
|
|
165
|
+
.menu-host {
|
|
166
|
+
position: relative;
|
|
167
|
+
display: inline-flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/* ===== GUI mode ===== */
|
|
172
|
+
.menu {
|
|
173
|
+
position: absolute;
|
|
174
|
+
z-index: var(--z-menu, 800);
|
|
175
|
+
background: var(--surface-1, #141414);
|
|
176
|
+
border: 1px solid var(--color-border, #2a2a2a);
|
|
177
|
+
border-radius: var(--radius-md, 10px);
|
|
178
|
+
box-shadow: var(--elevation-2, var(--shadow-md));
|
|
179
|
+
padding: var(--space-1, 4px);
|
|
180
|
+
min-width: 160px;
|
|
181
|
+
list-style: none;
|
|
182
|
+
margin: 0;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
.menu--bottom-start { top: calc(100% + 4px); left: 0; }
|
|
186
|
+
.menu--bottom-end { top: calc(100% + 4px); right: 0; }
|
|
187
|
+
.menu--top-start { bottom: calc(100% + 4px); left: 0; }
|
|
188
|
+
.menu--top-end { bottom: calc(100% + 4px); right: 0; }
|
|
189
|
+
|
|
190
|
+
.menu-backdrop {
|
|
191
|
+
position: fixed;
|
|
192
|
+
inset: 0;
|
|
193
|
+
z-index: calc(var(--z-menu, 800) - 1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.menu-item {
|
|
197
|
+
display: flex;
|
|
198
|
+
align-items: center;
|
|
199
|
+
gap: var(--space-2, 8px);
|
|
200
|
+
padding: var(--space-2, 8px) var(--space-3, 12px);
|
|
201
|
+
border-radius: var(--radius-sm, 6px);
|
|
202
|
+
font-size: var(--text-sm, 14px);
|
|
203
|
+
color: var(--color-text, #e8e8e8);
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
user-select: none;
|
|
206
|
+
outline: none;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.menu-item:hover:not(.disabled),
|
|
210
|
+
.menu-item.focused:not(.disabled) {
|
|
211
|
+
background: var(--alpha-10, rgba(255,255,255,0.1));
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.menu-item:focus-visible {
|
|
215
|
+
outline: var(--focus-ring-width, 3px) solid var(--color-focus-ring, #818cf8);
|
|
216
|
+
outline-offset: -1px;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.menu-item.disabled { opacity: 0.4; cursor: not-allowed; }
|
|
220
|
+
|
|
221
|
+
.menu-icon { width: 16px; text-align: center; font-size: 14px; }
|
|
222
|
+
.menu-label { flex: 1; }
|
|
223
|
+
.menu-shortcut { font-size: var(--text-xs, 12px); color: var(--color-text-muted, #888); }
|
|
224
|
+
|
|
225
|
+
.menu-separator {
|
|
226
|
+
height: 1px;
|
|
227
|
+
background: var(--color-border, #2a2a2a);
|
|
228
|
+
margin: var(--space-1, 4px) 0;
|
|
229
|
+
list-style: none;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* ===== TUI mode ===== */
|
|
233
|
+
.tui-menu {
|
|
234
|
+
list-style: none;
|
|
235
|
+
margin: 0;
|
|
236
|
+
padding: 0;
|
|
237
|
+
background: var(--tui-surface, #16213e);
|
|
238
|
+
border: 1px solid var(--tui-border, #0f3460);
|
|
239
|
+
font-family: var(--font-mono, monospace);
|
|
240
|
+
font-size: var(--text-sm, 14px);
|
|
241
|
+
color: var(--tui-text, #e0e0e0);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.tui-item {
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
gap: var(--space-2, 8px);
|
|
248
|
+
padding: 2px var(--space-2, 8px);
|
|
249
|
+
cursor: pointer;
|
|
250
|
+
outline: none;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
.tui-item:hover:not(.disabled),
|
|
254
|
+
.tui-item.focused:not(.disabled),
|
|
255
|
+
.tui-item:focus-visible:not(.disabled) {
|
|
256
|
+
background: var(--tui-highlight, #533483);
|
|
257
|
+
color: var(--tui-accent, #00d4ff);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.tui-item:focus-visible { outline: 1px solid var(--tui-accent, #00d4ff); }
|
|
261
|
+
|
|
262
|
+
.tui-item.disabled { opacity: 0.4; cursor: not-allowed; }
|
|
263
|
+
|
|
264
|
+
.tui-icon { width: 2ch; text-align: center; }
|
|
265
|
+
.tui-label { flex: 1; }
|
|
266
|
+
.tui-shortcut { color: var(--tui-text-dim, #888888); font-size: var(--text-xs, 12px); }
|
|
267
|
+
|
|
268
|
+
.tui-separator {
|
|
269
|
+
list-style: none;
|
|
270
|
+
color: var(--tui-border, #0f3460);
|
|
271
|
+
font-size: var(--text-xs, 12px);
|
|
272
|
+
user-select: none;
|
|
273
|
+
}
|
|
274
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { MenuItem } from "./Menu.types.js";
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Menu items. */
|
|
5
|
+
items: MenuItem[];
|
|
6
|
+
/** Control open state externally. */
|
|
7
|
+
open?: boolean;
|
|
8
|
+
/** Called when an item is selected. */
|
|
9
|
+
onselect?: (key: string) => void;
|
|
10
|
+
/** Called when the menu requests to close. */
|
|
11
|
+
onclose?: () => void;
|
|
12
|
+
/** Preferred opening position. Default: "bottom-start". */
|
|
13
|
+
position?: "bottom-start" | "bottom-end" | "top-start" | "top-end";
|
|
14
|
+
/** Enable TUI mode. */
|
|
15
|
+
tui?: boolean;
|
|
16
|
+
/** Trigger element. */
|
|
17
|
+
trigger: Snippet<[{
|
|
18
|
+
toggle: () => void;
|
|
19
|
+
isOpen: boolean;
|
|
20
|
+
}]>;
|
|
21
|
+
}
|
|
22
|
+
declare const Menu: import("svelte").Component<Props, {
|
|
23
|
+
MenuItem: typeof MenuItem;
|
|
24
|
+
}, "open">;
|
|
25
|
+
type Menu = ReturnType<typeof Menu>;
|
|
26
|
+
export default Menu;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Popover — Floating panel triggered by clicking an anchor element.
|
|
3
|
+
|
|
4
|
+
GUI mode: floating card with optional arrow, closes on outside click or Escape.
|
|
5
|
+
TUI mode: block-rendered panel with box-drawing border below/above the trigger.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { useTui } from "../useTui.js";
|
|
9
|
+
import type { Snippet } from "svelte";
|
|
10
|
+
|
|
11
|
+
interface Props {
|
|
12
|
+
/** Preferred side to open on. Default: "bottom". */
|
|
13
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
14
|
+
/** Control open state externally. */
|
|
15
|
+
open?: boolean;
|
|
16
|
+
/** Called when the popover requests to close. */
|
|
17
|
+
onclose?: () => void;
|
|
18
|
+
/** Enable TUI mode. */
|
|
19
|
+
tui?: boolean;
|
|
20
|
+
/** Trigger element — receives { toggle, isOpen }. Consumers must wire ARIA attributes (aria-expanded, aria-controls) to the trigger element. */
|
|
21
|
+
trigger: Snippet<[{ toggle: () => void; isOpen: boolean }]>;
|
|
22
|
+
/** Popover content. */
|
|
23
|
+
children: Snippet;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
let {
|
|
27
|
+
position = "bottom",
|
|
28
|
+
open = $bindable(false),
|
|
29
|
+
onclose,
|
|
30
|
+
tui = false,
|
|
31
|
+
trigger,
|
|
32
|
+
children,
|
|
33
|
+
}: Props = $props();
|
|
34
|
+
|
|
35
|
+
const getTuiCtx = useTui();
|
|
36
|
+
const isTui = $derived(tui || getTuiCtx());
|
|
37
|
+
|
|
38
|
+
function createId(prefix: string) {
|
|
39
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
|
|
40
|
+
return `${prefix}-${crypto.randomUUID()}`;
|
|
41
|
+
}
|
|
42
|
+
if (typeof crypto !== "undefined" && typeof crypto.getRandomValues === "function") {
|
|
43
|
+
const array = new Uint32Array(1);
|
|
44
|
+
crypto.getRandomValues(array);
|
|
45
|
+
return `${prefix}-${array[0].toString(36)}`;
|
|
46
|
+
}
|
|
47
|
+
return `${prefix}-${Date.now().toString(36)}`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const panelId = createId("popover");
|
|
51
|
+
|
|
52
|
+
function toggle() { open = !open; }
|
|
53
|
+
function close() { open = false; onclose?.(); }
|
|
54
|
+
|
|
55
|
+
function handleKeydown(e: KeyboardEvent) {
|
|
56
|
+
if (e.key === "Escape") close();
|
|
57
|
+
}
|
|
58
|
+
</script>
|
|
59
|
+
|
|
60
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
61
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
62
|
+
<div class="popover-host" class:tui={isTui} onkeydown={handleKeydown}>
|
|
63
|
+
{@render trigger({ toggle, isOpen: open })}
|
|
64
|
+
|
|
65
|
+
{#if open}
|
|
66
|
+
{#if isTui}
|
|
67
|
+
<div class="tui-popover" role="dialog" id={panelId}>
|
|
68
|
+
<div class="tui-popover-header">
|
|
69
|
+
<span class="tui-border-tl">╭</span>
|
|
70
|
+
<span class="tui-border-tr">╮</span>
|
|
71
|
+
</div>
|
|
72
|
+
<div class="tui-popover-body">
|
|
73
|
+
<span class="tui-border-l">│</span>
|
|
74
|
+
<div class="tui-popover-content">{@render children()}</div>
|
|
75
|
+
<span class="tui-border-r">│</span>
|
|
76
|
+
</div>
|
|
77
|
+
<div class="tui-popover-footer">
|
|
78
|
+
<span class="tui-border-bl">╰</span>
|
|
79
|
+
<span class="tui-border-br">╯</span>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
{:else}
|
|
83
|
+
<div class="popover popover--{position}" role="dialog" id={panelId} aria-modal="false">
|
|
84
|
+
{@render children()}
|
|
85
|
+
</div>
|
|
86
|
+
<!-- Outside-click backdrop (lightweight) -->
|
|
87
|
+
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
|
88
|
+
<div class="popover-backdrop" onclick={close} aria-hidden="true"></div>
|
|
89
|
+
{/if}
|
|
90
|
+
{/if}
|
|
91
|
+
</div>
|
|
92
|
+
|
|
93
|
+
<style>
|
|
94
|
+
.popover-host {
|
|
95
|
+
position: relative;
|
|
96
|
+
display: inline-flex;
|
|
97
|
+
flex-direction: column;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/* ===== GUI mode ===== */
|
|
101
|
+
.popover {
|
|
102
|
+
position: absolute;
|
|
103
|
+
z-index: var(--z-popover, 800);
|
|
104
|
+
background: var(--surface-1, #141414);
|
|
105
|
+
border: 1px solid var(--color-border, #2a2a2a);
|
|
106
|
+
border-radius: var(--radius-md, 10px);
|
|
107
|
+
box-shadow: var(--elevation-2, var(--shadow-md));
|
|
108
|
+
padding: var(--space-2, 8px);
|
|
109
|
+
min-width: 160px;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.popover--top { bottom: calc(100% + var(--space-1, 4px)); left: 0; }
|
|
113
|
+
.popover--bottom { top: calc(100% + var(--space-1, 4px)); left: 0; }
|
|
114
|
+
.popover--left { right: calc(100% + var(--space-1, 4px)); top: 0; }
|
|
115
|
+
.popover--right { left: calc(100% + var(--space-1, 4px)); top: 0; }
|
|
116
|
+
|
|
117
|
+
.popover-backdrop {
|
|
118
|
+
position: fixed;
|
|
119
|
+
inset: 0;
|
|
120
|
+
z-index: calc(var(--z-popover, 800) - 1);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* ===== TUI mode ===== */
|
|
124
|
+
.tui-popover {
|
|
125
|
+
background: var(--tui-surface, #16213e);
|
|
126
|
+
border: none;
|
|
127
|
+
font-family: var(--font-mono, monospace);
|
|
128
|
+
font-size: var(--text-sm, 14px);
|
|
129
|
+
color: var(--tui-text, #e0e0e0);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
.tui-popover-header,
|
|
133
|
+
.tui-popover-footer {
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: space-between;
|
|
136
|
+
color: var(--tui-border, #0f3460);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.tui-popover-body {
|
|
140
|
+
display: flex;
|
|
141
|
+
gap: var(--space-1, 4px);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
.tui-border-l,
|
|
145
|
+
.tui-border-r,
|
|
146
|
+
.tui-border-tl,
|
|
147
|
+
.tui-border-tr,
|
|
148
|
+
.tui-border-bl,
|
|
149
|
+
.tui-border-br {
|
|
150
|
+
color: var(--tui-border, #0f3460);
|
|
151
|
+
user-select: none;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.tui-popover-content {
|
|
155
|
+
flex: 1;
|
|
156
|
+
padding: var(--space-1, 4px) 0;
|
|
157
|
+
}
|
|
158
|
+
</style>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
interface Props {
|
|
3
|
+
/** Preferred side to open on. Default: "bottom". */
|
|
4
|
+
position?: "top" | "bottom" | "left" | "right";
|
|
5
|
+
/** Control open state externally. */
|
|
6
|
+
open?: boolean;
|
|
7
|
+
/** Called when the popover requests to close. */
|
|
8
|
+
onclose?: () => void;
|
|
9
|
+
/** Enable TUI mode. */
|
|
10
|
+
tui?: boolean;
|
|
11
|
+
/** Trigger element — receives { toggle, isOpen }. Consumers must wire ARIA attributes (aria-expanded, aria-controls) to the trigger element. */
|
|
12
|
+
trigger: Snippet<[{
|
|
13
|
+
toggle: () => void;
|
|
14
|
+
isOpen: boolean;
|
|
15
|
+
}]>;
|
|
16
|
+
/** Popover content. */
|
|
17
|
+
children: Snippet;
|
|
18
|
+
}
|
|
19
|
+
declare const Popover: import("svelte").Component<Props, {}, "open">;
|
|
20
|
+
type Popover = ReturnType<typeof Popover>;
|
|
21
|
+
export default Popover;
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
Toast — Non-blocking notification that auto-dismisses.
|
|
3
|
+
|
|
4
|
+
GUI mode: slides in from the corner, auto-dismisses after `duration` ms.
|
|
5
|
+
TUI mode: single-line status message at the bottom of the nearest positioned ancestor.
|
|
6
|
+
-->
|
|
7
|
+
<script lang="ts">
|
|
8
|
+
import { useTui } from "../useTui.js";
|
|
9
|
+
|
|
10
|
+
interface Props {
|
|
11
|
+
/** Notification message. */
|
|
12
|
+
message: string;
|
|
13
|
+
/** Visual variant. Default: "info". */
|
|
14
|
+
variant?: "info" | "success" | "warning" | "error";
|
|
15
|
+
/** Auto-dismiss delay in ms (0 = persistent). Default: 4000. */
|
|
16
|
+
duration?: number;
|
|
17
|
+
/** Position corner (GUI only). Default: "bottom-right". */
|
|
18
|
+
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
19
|
+
/** Whether the toast is visible. */
|
|
20
|
+
open?: boolean;
|
|
21
|
+
/** Called when the toast auto-closes or the user dismisses it. */
|
|
22
|
+
onclose?: () => void;
|
|
23
|
+
/** Enable TUI mode. */
|
|
24
|
+
tui?: boolean;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let {
|
|
28
|
+
message,
|
|
29
|
+
variant = "info",
|
|
30
|
+
duration = 4000,
|
|
31
|
+
position = "bottom-right",
|
|
32
|
+
open = $bindable(false),
|
|
33
|
+
onclose,
|
|
34
|
+
tui = false,
|
|
35
|
+
}: Props = $props();
|
|
36
|
+
|
|
37
|
+
const getTuiCtx = useTui();
|
|
38
|
+
const isTui = $derived(tui || getTuiCtx());
|
|
39
|
+
|
|
40
|
+
const variantIcon: Record<string, string> = {
|
|
41
|
+
info: "ℹ",
|
|
42
|
+
success: "✔",
|
|
43
|
+
warning: "⚠",
|
|
44
|
+
error: "✖",
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
function dismiss() {
|
|
48
|
+
open = false;
|
|
49
|
+
onclose?.();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
$effect(() => {
|
|
53
|
+
if (!open || duration === 0) return;
|
|
54
|
+
const timer = setTimeout(dismiss, duration);
|
|
55
|
+
return () => clearTimeout(timer);
|
|
56
|
+
});
|
|
57
|
+
</script>
|
|
58
|
+
|
|
59
|
+
{#if open}
|
|
60
|
+
{#if isTui}
|
|
61
|
+
<div
|
|
62
|
+
class="tui-toast tui-toast--{variant}"
|
|
63
|
+
role={variant === "error" || variant === "warning" ? "alert" : "status"}
|
|
64
|
+
aria-live={variant === "error" || variant === "warning" ? "assertive" : "polite"}
|
|
65
|
+
>
|
|
66
|
+
<span class="tui-icon" aria-hidden="true">{variantIcon[variant]}</span>
|
|
67
|
+
<span class="tui-message">{message}</span>
|
|
68
|
+
<button type="button" class="tui-dismiss" aria-label="Dismiss" onclick={dismiss}>✕</button>
|
|
69
|
+
</div>
|
|
70
|
+
{:else}
|
|
71
|
+
<div
|
|
72
|
+
class="toast toast--{variant} toast--{position}"
|
|
73
|
+
role={variant === "error" || variant === "warning" ? "alert" : "status"}
|
|
74
|
+
aria-live={variant === "error" || variant === "warning" ? "assertive" : "polite"}
|
|
75
|
+
>
|
|
76
|
+
<span class="toast-icon" aria-hidden="true">{variantIcon[variant]}</span>
|
|
77
|
+
<span class="toast-message">{message}</span>
|
|
78
|
+
<button type="button" class="toast-dismiss" aria-label="Dismiss" onclick={dismiss}>✕</button>
|
|
79
|
+
</div>
|
|
80
|
+
{/if}
|
|
81
|
+
{/if}
|
|
82
|
+
|
|
83
|
+
<style>
|
|
84
|
+
/* ===== GUI mode ===== */
|
|
85
|
+
.toast {
|
|
86
|
+
position: fixed;
|
|
87
|
+
z-index: var(--z-toast, 1100);
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: var(--space-2, 8px);
|
|
91
|
+
padding: var(--space-3, 12px) var(--space-4, 16px);
|
|
92
|
+
background: var(--surface-2, #1e1e1e);
|
|
93
|
+
border: 1px solid var(--color-border, #2a2a2a);
|
|
94
|
+
border-radius: var(--radius-md, 10px);
|
|
95
|
+
box-shadow: var(--elevation-2, var(--shadow-md));
|
|
96
|
+
font-size: var(--text-sm, 14px);
|
|
97
|
+
color: var(--color-text, #e8e8e8);
|
|
98
|
+
max-width: 360px;
|
|
99
|
+
animation: toast-in 0.2s ease;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@keyframes toast-in {
|
|
103
|
+
from { opacity: 0; transform: translateY(8px); }
|
|
104
|
+
to { opacity: 1; transform: translateY(0); }
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.toast--top-right { top: var(--space-4, 16px); right: var(--space-4, 16px); }
|
|
108
|
+
.toast--top-left { top: var(--space-4, 16px); left: var(--space-4, 16px); }
|
|
109
|
+
.toast--bottom-right { bottom: var(--space-4, 16px); right: var(--space-4, 16px); }
|
|
110
|
+
.toast--bottom-left { bottom: var(--space-4, 16px); left: var(--space-4, 16px); }
|
|
111
|
+
|
|
112
|
+
.toast--info { border-left: 3px solid var(--color-info, #3b82f6); }
|
|
113
|
+
.toast--success { border-left: 3px solid var(--color-success, #22c55e); }
|
|
114
|
+
.toast--warning { border-left: 3px solid var(--color-warning, #f59e0b); }
|
|
115
|
+
.toast--error { border-left: 3px solid var(--color-danger, #ef4444); }
|
|
116
|
+
|
|
117
|
+
.toast-icon {
|
|
118
|
+
font-size: var(--text-base, 16px);
|
|
119
|
+
flex-shrink: 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.toast--info .toast-icon { color: var(--color-info, #3b82f6); }
|
|
123
|
+
.toast--success .toast-icon { color: var(--color-success, #22c55e); }
|
|
124
|
+
.toast--warning .toast-icon { color: var(--color-warning, #f59e0b); }
|
|
125
|
+
.toast--error .toast-icon { color: var(--color-danger, #ef4444); }
|
|
126
|
+
|
|
127
|
+
.toast-message { flex: 1; }
|
|
128
|
+
|
|
129
|
+
.toast-dismiss {
|
|
130
|
+
background: none;
|
|
131
|
+
border: none;
|
|
132
|
+
cursor: pointer;
|
|
133
|
+
color: var(--color-text-muted, #888);
|
|
134
|
+
font-size: 12px;
|
|
135
|
+
padding: 0;
|
|
136
|
+
line-height: 1;
|
|
137
|
+
flex-shrink: 0;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
.toast-dismiss:hover { color: var(--color-text, #e8e8e8); }
|
|
141
|
+
.toast-dismiss:focus-visible {
|
|
142
|
+
outline: var(--focus-ring-width, 3px) solid var(--color-focus-ring, #818cf8);
|
|
143
|
+
outline-offset: var(--focus-ring-offset, 2px);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/* ===== TUI mode ===== */
|
|
147
|
+
.tui-toast {
|
|
148
|
+
display: flex;
|
|
149
|
+
align-items: center;
|
|
150
|
+
gap: var(--space-2, 8px);
|
|
151
|
+
padding: 0 var(--space-2, 8px);
|
|
152
|
+
font-family: var(--font-mono, monospace);
|
|
153
|
+
font-size: var(--text-sm, 14px);
|
|
154
|
+
color: var(--tui-text, #e0e0e0);
|
|
155
|
+
background: var(--tui-surface, #16213e);
|
|
156
|
+
border-top: 1px solid var(--tui-border, #0f3460);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.tui-toast--info .tui-icon { color: var(--color-info, #3b82f6); }
|
|
160
|
+
.tui-toast--success .tui-icon { color: var(--tui-success, #00ff88); }
|
|
161
|
+
.tui-toast--warning .tui-icon { color: var(--tui-warning, #ffaa00); }
|
|
162
|
+
.tui-toast--error .tui-icon { color: var(--tui-error, #ff4444); }
|
|
163
|
+
|
|
164
|
+
.tui-message { flex: 1; }
|
|
165
|
+
|
|
166
|
+
.tui-dismiss {
|
|
167
|
+
background: none;
|
|
168
|
+
border: none;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
color: var(--tui-text-dim, #888888);
|
|
171
|
+
font-family: inherit;
|
|
172
|
+
font-size: 12px;
|
|
173
|
+
padding: 0;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.tui-dismiss:focus-visible {
|
|
177
|
+
outline: 1px solid var(--tui-accent, #00d4ff);
|
|
178
|
+
}
|
|
179
|
+
</style>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
interface Props {
|
|
2
|
+
/** Notification message. */
|
|
3
|
+
message: string;
|
|
4
|
+
/** Visual variant. Default: "info". */
|
|
5
|
+
variant?: "info" | "success" | "warning" | "error";
|
|
6
|
+
/** Auto-dismiss delay in ms (0 = persistent). Default: 4000. */
|
|
7
|
+
duration?: number;
|
|
8
|
+
/** Position corner (GUI only). Default: "bottom-right". */
|
|
9
|
+
position?: "top-right" | "top-left" | "bottom-right" | "bottom-left";
|
|
10
|
+
/** Whether the toast is visible. */
|
|
11
|
+
open?: boolean;
|
|
12
|
+
/** Called when the toast auto-closes or the user dismisses it. */
|
|
13
|
+
onclose?: () => void;
|
|
14
|
+
/** Enable TUI mode. */
|
|
15
|
+
tui?: boolean;
|
|
16
|
+
}
|
|
17
|
+
declare const Toast: import("svelte").Component<Props, {}, "open">;
|
|
18
|
+
type Toast = ReturnType<typeof Toast>;
|
|
19
|
+
export default Toast;
|