@stephen-netu/design-system 0.1.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/README.md +66 -0
- package/package.json +130 -0
- package/src/b0-app-shell/clarification-overlay/ClarificationOverlay.svelte +221 -0
- package/src/b0-app-shell/clarification-overlay/index.ts +1 -0
- package/src/b0-app-shell/command-bar/CommandBar.svelte +565 -0
- package/src/b0-app-shell/index.ts +14 -0
- package/src/base.css +152 -0
- package/src/d0-data-viz/BarChart.svelte +236 -0
- package/src/d0-data-viz/CheckpointBar.svelte +574 -0
- package/src/d0-data-viz/CheckpointPill.svelte +297 -0
- package/src/d0-data-viz/MetricCard.svelte +159 -0
- package/src/d0-data-viz/SparkLine.svelte +66 -0
- package/src/d0-data-viz/StorageMap.svelte +182 -0
- package/src/d0-data-viz/Timeline.svelte +188 -0
- package/src/d0-data-viz/checkpoint-types.ts +162 -0
- package/src/d0-data-viz/d0-tokens.css +32 -0
- package/src/d0-data-viz/index.ts +58 -0
- package/src/d0-data-viz/storage-map-types.ts +142 -0
- package/src/d0-data-viz/theme-integration.ts +134 -0
- package/src/d0-data-viz/types.ts +53 -0
- package/src/demos/BspSizingInfographic.svelte +386 -0
- package/src/demos/BspZoneDemo.svelte +255 -0
- package/src/demos/ControlRoomDemo.svelte +570 -0
- package/src/demos/NodepadBlocksShowcase.svelte +189 -0
- package/src/demos/index.ts +7 -0
- package/src/editor/CodeEditor.svelte +188 -0
- package/src/editor/FindReplaceDialog.svelte +516 -0
- package/src/editor/MarkdownEditor.svelte +660 -0
- package/src/editor/index.ts +2 -0
- package/src/entity-graph-view/WorldView.svelte +171 -0
- package/src/entity-graph-view/index.ts +6 -0
- package/src/entity-graph-view/types.ts +364 -0
- package/src/epistemic/index.ts +2 -0
- package/src/epistemic/types.ts +134 -0
- package/src/epistemic/utils.ts +32 -0
- package/src/f0-forms/checkbox/Checkbox.svelte +158 -0
- package/src/f0-forms/field/FormField.svelte +91 -0
- package/src/f0-forms/index.ts +11 -0
- package/src/f0-forms/radio/RadioGroup.svelte +192 -0
- package/src/f0-forms/search/SearchBar.svelte +99 -0
- package/src/f0-forms/select/Select.svelte +198 -0
- package/src/f0-forms/textarea/TextArea.svelte +170 -0
- package/src/f0-forms/toggle/FormToggle.svelte +147 -0
- package/src/f0-forms/validation/validate.ts +47 -0
- package/src/fabric/ai/ChatPanel.svelte +648 -0
- package/src/fabric/ai/index.ts +11 -0
- package/src/fabric/data/index.ts +21 -0
- package/src/fabric/feedback/StatusBar.svelte +274 -0
- package/src/fabric/feedback/ToastContainer.svelte +122 -0
- package/src/fabric/feedback/index.ts +14 -0
- package/src/fabric/index.ts +37 -0
- package/src/fabric/input/CommandBar.svelte +79 -0
- package/src/fabric/input/KeyboardShortcuts.svelte +388 -0
- package/src/fabric/input/index.ts +12 -0
- package/src/fabric/layout/GlassBox.svelte +193 -0
- package/src/fabric/layout/KanbanBoard.svelte +158 -0
- package/src/fabric/layout/KanbanCard.svelte +226 -0
- package/src/fabric/layout/KanbanColumn.svelte +278 -0
- package/src/fabric/layout/index.ts +17 -0
- package/src/fabric/navigation/ActivityRail.svelte +271 -0
- package/src/fabric/navigation/index.ts +11 -0
- package/src/g0-geo/SovereignMap.svelte +149 -0
- package/src/g0-geo/index.ts +2 -0
- package/src/index.ts +68 -0
- package/src/l0-layout/block-writer/BlockConnectionLines.svelte +98 -0
- package/src/l0-layout/block-writer/BlockContent.svelte +213 -0
- package/src/l0-layout/block-writer/BlockHeader.svelte +230 -0
- package/src/l0-layout/block-writer/BlockItem.svelte +177 -0
- package/src/l0-layout/block-writer/BlockWriter.svelte +219 -0
- package/src/l0-layout/block-writer/block-writer-types.ts +63 -0
- package/src/l0-layout/block-writer/index.ts +24 -0
- package/src/l0-layout/block-writer/markdown.ts +110 -0
- package/src/l0-layout/chat-view/ChatViewLayout.svelte +61 -0
- package/src/l0-layout/epistemic/AllayDetailPanel.svelte +113 -0
- package/src/l0-layout/epistemic/AssayDetailPanel.svelte +164 -0
- package/src/l0-layout/epistemic/AvenueTrace.svelte +253 -0
- package/src/l0-layout/epistemic/KnowledgeDetailPanel.svelte +204 -0
- package/src/l0-layout/epistemic/ParadigmLens.svelte +170 -0
- package/src/l0-layout/epistemic/ProvenanceChain.svelte +164 -0
- package/src/l0-layout/epistemic/TrustView.svelte +195 -0
- package/src/l0-layout/epistemic/index.ts +7 -0
- package/src/l0-layout/grove/AccordCard.svelte +156 -0
- package/src/l0-layout/grove/GroveCard.svelte +197 -0
- package/src/l0-layout/grove/MemberBadge.svelte +96 -0
- package/src/l0-layout/grove/MemberList.svelte +59 -0
- package/src/l0-layout/grove/grove-types.ts +29 -0
- package/src/l0-layout/grove/index.ts +14 -0
- package/src/l0-layout/index.ts +45 -0
- package/src/l0-layout/kanban/KanbanBoard.svelte +71 -0
- package/src/l0-layout/kanban/KanbanCard.svelte +276 -0
- package/src/l0-layout/kanban/KanbanColumn.svelte +198 -0
- package/src/l0-layout/kanban/KanbanViewLayout.svelte +61 -0
- package/src/l0-layout/workspace/WorkspaceLayout.svelte +93 -0
- package/src/n0-node-graph/GraphEditor.svelte +342 -0
- package/src/n0-node-graph/GraphHistory.svelte +122 -0
- package/src/n0-node-graph/Node.svelte +269 -0
- package/src/n0-node-graph/NodeCanvas.svelte +187 -0
- package/src/n0-node-graph/NodeEdge.svelte +152 -0
- package/src/n0-node-graph/NodePalette.svelte +318 -0
- package/src/n0-node-graph/NodePort.svelte +128 -0
- package/src/n0-node-graph/index.ts +33 -0
- package/src/n0-node-graph/n0-tokens.css +29 -0
- package/src/n0-node-graph/node-port-types.ts +19 -0
- package/src/n0-node-graph/types.ts +96 -0
- package/src/n0-node-graph/validate.test.ts +722 -0
- package/src/n0-node-graph/validate.ts +371 -0
- package/src/n1-flow-canvas/FlowCanvas.svelte +202 -0
- package/src/n1-flow-canvas/graph-state.svelte.ts +95 -0
- package/src/n1-flow-canvas/index.ts +21 -0
- package/src/n1-flow-canvas/layout.ts +118 -0
- package/src/n1-flow-canvas/layout.worker.ts +128 -0
- package/src/n1-flow-canvas/types.ts +102 -0
- package/src/n1-flow-canvas/utils.ts +146 -0
- package/src/n2-manifold-disk/ManifoldDisk.svelte +314 -0
- package/src/n2-manifold-disk/index.ts +6 -0
- package/src/n2-manifold-disk/poincare.ts +184 -0
- package/src/n3-force-graph/ForceGraph.svelte +180 -0
- package/src/n3-force-graph/GraphTooltip.svelte +99 -0
- package/src/n3-force-graph/animated-edge.ts +107 -0
- package/src/n3-force-graph/health-node.ts +128 -0
- package/src/n3-force-graph/index.ts +31 -0
- package/src/n3-force-graph/types.ts +94 -0
- package/src/n4-adaptive-manifold/AdaptiveManifold.svelte +1165 -0
- package/src/n4-adaptive-manifold/DetailCard.svelte +212 -0
- package/src/n4-adaptive-manifold/SuggestionIndicator.svelte +91 -0
- package/src/n4-adaptive-manifold/classifier.ts +139 -0
- package/src/n4-adaptive-manifold/index.ts +45 -0
- package/src/n4-adaptive-manifold/n4-tokens.css +155 -0
- package/src/n4-adaptive-manifold/orchestrator.ts +301 -0
- package/src/n4-adaptive-manifold/phase-theme.ts +92 -0
- package/src/n4-adaptive-manifold/pinnacle-events.ts +135 -0
- package/src/n4-adaptive-manifold/providers/gravitational-provider.ts +191 -0
- package/src/n4-adaptive-manifold/providers/manifold-provider.ts +276 -0
- package/src/n4-adaptive-manifold/transition.ts +90 -0
- package/src/n4-adaptive-manifold/types.ts +313 -0
- package/src/p0-primitives/index.ts +6 -0
- package/src/p0-primitives/tokens/control-room.css +309 -0
- package/src/p0-primitives/tokens/index.ts +6 -0
- package/src/p0-primitives/tokens/tokens.css +140 -0
- package/src/p0-primitives/tokens/tokens.json +132 -0
- package/src/p0-primitives/types/index.ts +123 -0
- package/src/p0-primitives/types/plugin-types.ts +74 -0
- package/src/p0-primitives/utils/index.ts +39 -0
- package/src/platform/hud-protocol.ts +20 -0
- package/src/platform/index.ts +3 -0
- package/src/s0-state/create-store.ts +84 -0
- package/src/s0-state/derived.ts +41 -0
- package/src/s0-state/index.ts +6 -0
- package/src/showcase/Showcase.svelte +401 -0
- package/src/spatial/BspTilingCanvas.svelte +232 -0
- package/src/spatial/ForceCanvas.svelte +289 -0
- package/src/spatial/LodRenderer.svelte +334 -0
- package/src/spatial/SnapZoomCamera.svelte +185 -0
- package/src/spatial/ZoneTiler.svelte +311 -0
- package/src/spatial/camera-types.ts +45 -0
- package/src/spatial/content-proportional-bsp.ts +253 -0
- package/src/spatial/force-canvas-types.ts +102 -0
- package/src/spatial/index.ts +15 -0
- package/src/spatial/lod-types.ts +70 -0
- package/src/spatial/zone-tiler-types.ts +177 -0
- package/src/stores/vaultStore.svelte +14 -0
- package/src/t0-transport/connection.ts +34 -0
- package/src/t0-transport/error.ts +37 -0
- package/src/t0-transport/events.ts +50 -0
- package/src/t0-transport/index.ts +6 -0
- package/src/t0-transport/invoke.ts +30 -0
- package/src/tauri/index.ts +13 -0
- package/src/u0-primitives/allay-gauge/AllayGauge.svelte +90 -0
- package/src/u0-primitives/allay-gauge/index.ts +1 -0
- package/src/u0-primitives/animated-icon/AnimatedIcon.svelte +273 -0
- package/src/u0-primitives/animated-icon/README.md +53 -0
- package/src/u0-primitives/animated-icon/index.ts +31 -0
- package/src/u0-primitives/assay-badge/AssayBadge.svelte +121 -0
- package/src/u0-primitives/assay-badge/AssaySignalBar.svelte +83 -0
- package/src/u0-primitives/assay-badge/index.ts +2 -0
- package/src/u0-primitives/avatar/Avatar.svelte +177 -0
- package/src/u0-primitives/badge/Badge.svelte +149 -0
- package/src/u0-primitives/button/Button.svelte +304 -0
- package/src/u0-primitives/card/Card.svelte +182 -0
- package/src/u0-primitives/dropdown/Dropdown.svelte +305 -0
- package/src/u0-primitives/expert-badge/ExpertBadge.svelte +89 -0
- package/src/u0-primitives/expert-badge/index.ts +1 -0
- package/src/u0-primitives/index.ts +35 -0
- package/src/u0-primitives/input/Input.svelte +237 -0
- package/src/u0-primitives/modal/Modal.svelte +331 -0
- package/src/u0-primitives/modal/index.ts +1 -0
- package/src/u0-primitives/spinner/Spinner.svelte +129 -0
- package/src/u0-primitives/tabs/Tabs.svelte +225 -0
- package/src/u0-primitives/tabs/tabs-types.ts +11 -0
- package/src/u0-primitives/toggle/Toggle.svelte +161 -0
- package/src/u0-primitives/tooltip/Tooltip.svelte +234 -0
- package/src/v0-diagrams/DiagramPanel.svelte +68 -0
- package/src/v0-diagrams/MarkmapDiagram.svelte +56 -0
- package/src/v0-diagrams/MermaidDiagram.svelte +53 -0
- package/src/v0-diagrams/index.ts +4 -0
- package/src/v0-diagrams/types.ts +28 -0
- package/src/v0-diagrams/v0-tokens.css +6 -0
- package/src/vite-env.d.ts +7 -0
- package/src/x0-enchanted-blocks/ENCHANTED_BLOCKS_SPEC.md +457 -0
- package/src/x0-enchanted-blocks/components/EnchantedBlock.svelte +485 -0
- package/src/x0-enchanted-blocks/components/NodepadBlock.svelte +262 -0
- package/src/x0-enchanted-blocks/core/block-layout.ts +211 -0
- package/src/x0-enchanted-blocks/core/block-types.ts +227 -0
- package/src/x0-enchanted-blocks/index.ts +46 -0
|
@@ -0,0 +1,401 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
Button, Input, Card, Badge, Avatar, Toggle, Dropdown, Tooltip, Tabs, Spinner,
|
|
4
|
+
FormField, SearchBar, KanbanCard, KanbanColumn, KanbanBoard,
|
|
5
|
+
WorkspaceLayout, ChatViewLayout, KanbanViewLayout
|
|
6
|
+
} from '../index.js';
|
|
7
|
+
import { CheckpointBar } from '../d0-data-viz';
|
|
8
|
+
import type { Checkpoint } from '../d0-data-viz/checkpoint-types';
|
|
9
|
+
import { Search, Plus, Settings, MessageSquare, Bell, Moon, Sun, ArrowRight, Smile, MoreHorizontal, PenLine, Trash2 } from '@lucide/svelte';
|
|
10
|
+
|
|
11
|
+
let isDarkTheme = $state(true);
|
|
12
|
+
|
|
13
|
+
$effect(() => {
|
|
14
|
+
document.documentElement.setAttribute('data-theme', isDarkTheme ? 'dark' : 'light');
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
// Example state for showcase
|
|
18
|
+
let isSidebarCollapsed = $state(false);
|
|
19
|
+
let isContextPanelOpen = $state(false);
|
|
20
|
+
let activeTab = $state('components');
|
|
21
|
+
let inputValue = $state('');
|
|
22
|
+
let toggleChecked = $state(true);
|
|
23
|
+
|
|
24
|
+
const demoTabs = [
|
|
25
|
+
{ id: 'components', label: 'Atomic Components' },
|
|
26
|
+
{ id: 'molecules', label: 'Molecules' },
|
|
27
|
+
{ id: 'organisms', label: 'Organisms & Layouts' },
|
|
28
|
+
{ id: 'dataviz', label: 'Data Viz' }
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
// Demo checkpoints for CheckpointBar
|
|
32
|
+
const demoCheckpoints: Checkpoint[] = [
|
|
33
|
+
{ seqno: 1, id: 'genesis', component: 'system-init', componentType: 'system-snapshot', status: 'committed', description: 'System initialization', timestamp: 1712000000000 },
|
|
34
|
+
{ seqno: 23, id: 'cp-23', component: 'leap-shell', componentType: 'ui-bundle', status: 'committed', description: 'Add navigation rail', version: { from: '0.1.0', to: '0.2.0' }, author: 'agent-cascade', timestamp: 1713000000000 },
|
|
35
|
+
{ seqno: 42, id: 'cp-42', component: 'sovereign-kernel', componentType: 'rust-crate', status: 'system', description: 'Kernel security patch', author: 'netu', timestamp: 1714000000000 },
|
|
36
|
+
{ seqno: 78, id: 'cp-78', component: 'agora-voice', componentType: 'ui-bundle', status: 'committed', description: 'Voice channel UI', version: { from: '1.0.0', to: '1.1.0' }, author: 'agent-ryu', timestamp: 1715000000000 },
|
|
37
|
+
{ seqno: 102, id: 'cp-102', component: 'substrate-daemon', componentType: 'substrate-binary', status: 'committed', description: 'Daemon auto-restart', author: 'netu', timestamp: 1716000000000 },
|
|
38
|
+
{ seqno: 156, id: 'cp-156', component: 'realm-config', componentType: 'config', status: 'proposed', description: 'New governance rules', author: 'agent-plex', timestamp: 1717000000000 },
|
|
39
|
+
];
|
|
40
|
+
let demoCurrentSeqno = $state(78);
|
|
41
|
+
let demoPreviewSeqno = $state<number | null>(null);
|
|
42
|
+
|
|
43
|
+
const dropdownItems = [
|
|
44
|
+
{ id: '1', label: 'Edit Profile', shortcut: '⌘E' },
|
|
45
|
+
{ id: '2', label: 'Preferences', shortcut: '⌘,' },
|
|
46
|
+
{ id: '3', label: 'Log out', danger: true }
|
|
47
|
+
];
|
|
48
|
+
</script>
|
|
49
|
+
|
|
50
|
+
<div class="showcase ds-stack">
|
|
51
|
+
<div class="showcase-header">
|
|
52
|
+
<div class="ds-cluster">
|
|
53
|
+
<h1>KOS Design System</h1>
|
|
54
|
+
<Badge color="accent" variant="status">v1.0.0</Badge>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="ds-cluster">
|
|
57
|
+
<Toggle bind:checked={isDarkTheme} size="sm">
|
|
58
|
+
{#snippet label()}
|
|
59
|
+
<span>{isDarkTheme ? 'Dark' : 'Light'}</span>
|
|
60
|
+
{/snippet}
|
|
61
|
+
</Toggle>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
|
|
65
|
+
<Tabs tabs={demoTabs} bind:activeId={activeTab} />
|
|
66
|
+
|
|
67
|
+
<div class="showcase-content">
|
|
68
|
+
|
|
69
|
+
<!-- ATOMIC COMPONENTS -->
|
|
70
|
+
{#if activeTab === 'components'}
|
|
71
|
+
<section class="ds-stack" style="gap: var(--space-8)">
|
|
72
|
+
|
|
73
|
+
<div class="component-section">
|
|
74
|
+
<h3>Buttons</h3>
|
|
75
|
+
<div class="demo-row">
|
|
76
|
+
<Button variant="primary">Primary</Button>
|
|
77
|
+
<Button variant="secondary">Secondary</Button>
|
|
78
|
+
<Button variant="ghost">Ghost</Button>
|
|
79
|
+
<Button variant="danger">Danger</Button>
|
|
80
|
+
<Button variant="primary" loading>Loading</Button>
|
|
81
|
+
<Button variant="primary" size="sm">Small</Button>
|
|
82
|
+
<Button variant="primary" size="lg">Large</Button>
|
|
83
|
+
<Button variant="secondary">
|
|
84
|
+
{#snippet iconLeading()} <Plus size={16}/> {/snippet}
|
|
85
|
+
With Icon
|
|
86
|
+
</Button>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<div class="component-section">
|
|
91
|
+
<h3>Inputs & Forms</h3>
|
|
92
|
+
<div class="demo-row" style="max-width: 400px; display: flex; flex-direction: column;">
|
|
93
|
+
<Input placeholder="Standard Input" bind:value={inputValue} />
|
|
94
|
+
<Input placeholder="With leading icon">
|
|
95
|
+
{#snippet iconLeading()} <Search size={16}/> {/snippet}
|
|
96
|
+
</Input>
|
|
97
|
+
<Input type="password" placeholder="Password input" />
|
|
98
|
+
<Input error placeholder="Error state" />
|
|
99
|
+
<Input disabled placeholder="Disabled state" />
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<div class="component-section">
|
|
104
|
+
<h3>Cards</h3>
|
|
105
|
+
<div class="demo-row">
|
|
106
|
+
<Card variant="elevated">
|
|
107
|
+
{#snippet header()} Elevated Card {/snippet}
|
|
108
|
+
<p>Default card style for surfaces.</p>
|
|
109
|
+
</Card>
|
|
110
|
+
|
|
111
|
+
<Card variant="flat">
|
|
112
|
+
{#snippet header()} Flat Card {/snippet}
|
|
113
|
+
<p>Subtle border, no shadow.</p>
|
|
114
|
+
</Card>
|
|
115
|
+
|
|
116
|
+
<Card variant="interactive" selected>
|
|
117
|
+
{#snippet header()} Interactive (Selected) {/snippet}
|
|
118
|
+
<p>Hover and focus effects with accent ring.</p>
|
|
119
|
+
</Card>
|
|
120
|
+
|
|
121
|
+
<div class="glass-bg">
|
|
122
|
+
<Card variant="glass">
|
|
123
|
+
{#snippet header()} Glassmorphic {/snippet}
|
|
124
|
+
<p>For overlays and floating elements over content.</p>
|
|
125
|
+
</Card>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
</div>
|
|
129
|
+
|
|
130
|
+
<div class="component-section">
|
|
131
|
+
<h3>Badges & Avatars</h3>
|
|
132
|
+
<div class="demo-row">
|
|
133
|
+
<div class="ds-cluster">
|
|
134
|
+
<Badge variant="status" color="accent">Active</Badge>
|
|
135
|
+
<Badge variant="status" color="success">Paid</Badge>
|
|
136
|
+
<Badge variant="status" color="warning">Pending</Badge>
|
|
137
|
+
<Badge variant="status" color="error">Failed</Badge>
|
|
138
|
+
<Badge variant="status" color="neutral">Draft</Badge>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
<div class="ds-cluster">
|
|
142
|
+
<Badge variant="count" color="accent">12</Badge>
|
|
143
|
+
<Badge variant="count" color="error">3</Badge>
|
|
144
|
+
<Badge variant="outline" color="info">Outline</Badge>
|
|
145
|
+
<Badge variant="dot" color="success" />
|
|
146
|
+
</div>
|
|
147
|
+
|
|
148
|
+
<div class="ds-cluster" style="margin-left: var(--space-8)">
|
|
149
|
+
<Avatar name="Netu" status="online" size="lg" />
|
|
150
|
+
<Avatar name="System Architect" status="typing" />
|
|
151
|
+
<Avatar name="Bot Agent" status="offline" size="sm" />
|
|
152
|
+
</div>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="component-section">
|
|
157
|
+
<h3>Interactive Atoms</h3>
|
|
158
|
+
<div class="demo-row">
|
|
159
|
+
<Toggle bind:checked={toggleChecked} />
|
|
160
|
+
|
|
161
|
+
<Dropdown items={dropdownItems}>
|
|
162
|
+
{#snippet trigger()}
|
|
163
|
+
<Button variant="secondary">Open Menu</Button>
|
|
164
|
+
{/snippet}
|
|
165
|
+
</Dropdown>
|
|
166
|
+
|
|
167
|
+
<Tooltip content="This is a helpful tooltip!">
|
|
168
|
+
{#snippet trigger()}
|
|
169
|
+
<Badge variant="outline" color="accent">Hover me</Badge>
|
|
170
|
+
{/snippet}
|
|
171
|
+
</Tooltip>
|
|
172
|
+
|
|
173
|
+
<Spinner size="lg" color="accent" />
|
|
174
|
+
</div>
|
|
175
|
+
</div>
|
|
176
|
+
</section>
|
|
177
|
+
{/if}
|
|
178
|
+
|
|
179
|
+
<!-- MOLECULES -->
|
|
180
|
+
{#if activeTab === 'molecules'}
|
|
181
|
+
<section class="ds-stack" style="gap: var(--space-8)">
|
|
182
|
+
|
|
183
|
+
<div class="component-section">
|
|
184
|
+
<h3>Form Field & Search</h3>
|
|
185
|
+
<div class="demo-row" style="flex-direction: column; max-width: 400px;">
|
|
186
|
+
<FormField id="demo-field" label="Email Address" helper="We'll never share your email.">
|
|
187
|
+
<Input id="demo-field" type="email" placeholder="agent@kos.network" />
|
|
188
|
+
</FormField>
|
|
189
|
+
|
|
190
|
+
<FormField id="demo-err" label="Username" error="This username is taken.">
|
|
191
|
+
<Input id="demo-err" error value="sovra_admin" />
|
|
192
|
+
</FormField>
|
|
193
|
+
|
|
194
|
+
<SearchBar placeholder="Search globally..." />
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
|
|
198
|
+
<!-- TODO: Re-add when Chat components exist
|
|
199
|
+
<div class="component-section">
|
|
200
|
+
<h3>List Items</h3>
|
|
201
|
+
<div class="demo-row" style="flex-direction: column; max-width: 320px;">
|
|
202
|
+
<ChannelListItem id="c1" name="general-chat" time="2m" unreadCount={5} preview="Hey, did we merge the PR?" />
|
|
203
|
+
<ChannelListItem id="c2" name="architecture-req" isActive preview="Let's discuss the capability model." />
|
|
204
|
+
<ChannelListItem id="c3" name="alerts-noisy" isMuted unreadCount={12} preview="[CRITICAL] Pod failure" />
|
|
205
|
+
</div>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div class="component-section">
|
|
209
|
+
<h3>Message Bubbles</h3>
|
|
210
|
+
<div class="demo-row" style="flex-direction: column; max-width: 500px">
|
|
211
|
+
<MessageBubble id="m1" senderName="Alice" timestamp={Date.now() - 60000} content="..." />
|
|
212
|
+
</div>
|
|
213
|
+
</div>
|
|
214
|
+
-->
|
|
215
|
+
|
|
216
|
+
<div class="component-section">
|
|
217
|
+
<h3>Kanban Card</h3>
|
|
218
|
+
<div class="demo-row">
|
|
219
|
+
<div style="width: 280px">
|
|
220
|
+
<KanbanCard item={{
|
|
221
|
+
id: '1', title: 'Implement Design Tokens', priority: 'high',
|
|
222
|
+
content: 'Add full css variable support to all atomic components.',
|
|
223
|
+
badges: [{ label: 'Frontend', color: 'accent' }],
|
|
224
|
+
assignee: { name: 'Netu' }
|
|
225
|
+
}} />
|
|
226
|
+
</div>
|
|
227
|
+
</div>
|
|
228
|
+
</div>
|
|
229
|
+
</section>
|
|
230
|
+
{/if}
|
|
231
|
+
<!-- ORGANISMS & LAYOUTS -->
|
|
232
|
+
{#if activeTab === 'organisms'}
|
|
233
|
+
<section class="ds-stack" style="gap: var(--space-8)">
|
|
234
|
+
|
|
235
|
+
<!-- TODO: Re-add when ContextPanel component exists
|
|
236
|
+
<div class="component-section">
|
|
237
|
+
<h3>Context Panel</h3>
|
|
238
|
+
<div style="height: 300px; position: relative; border: 1px solid var(--border-default); overflow: hidden; border-radius: var(--radius-lg, 8px);">
|
|
239
|
+
<div style="padding: 20px;">
|
|
240
|
+
<Button onclick={() => isContextPanelOpen = true}>Open Panel</Button>
|
|
241
|
+
</div>
|
|
242
|
+
<ContextPanel bind:isOpen={isContextPanelOpen} title="Task Details" tabs={[]}>
|
|
243
|
+
<div class="ds-stack">
|
|
244
|
+
<KanbanCard item={{id:'x', title:'Sample Task', priority:'urgent'}} />
|
|
245
|
+
</div>
|
|
246
|
+
</ContextPanel>
|
|
247
|
+
</div>
|
|
248
|
+
</div>
|
|
249
|
+
-->
|
|
250
|
+
|
|
251
|
+
<div class="component-section">
|
|
252
|
+
<h3>Kanban Board</h3>
|
|
253
|
+
<div style="height: 400px; border: 1px solid var(--border-default); border-radius: var(--radius-lg, 8px);">
|
|
254
|
+
<KanbanBoard>
|
|
255
|
+
<KanbanColumn id="todo" title="To Do" count={2}>
|
|
256
|
+
<KanbanCard item={{id:'t1', title:'Setup repo', priority:'high'}} />
|
|
257
|
+
<KanbanCard item={{id:'t2', title:'Config vite', priority:'medium'}} />
|
|
258
|
+
</KanbanColumn>
|
|
259
|
+
|
|
260
|
+
<KanbanColumn id="inprog" title="In Progress" count={1} wipLimit={3}>
|
|
261
|
+
<KanbanCard item={{id:'t3', title:'Build components', priority:'high'}} />
|
|
262
|
+
</KanbanColumn>
|
|
263
|
+
|
|
264
|
+
<KanbanColumn id="done" title="Done" count={0} />
|
|
265
|
+
</KanbanBoard>
|
|
266
|
+
</div>
|
|
267
|
+
</div>
|
|
268
|
+
|
|
269
|
+
<!-- TODO: Re-add when ChatPanel and CanvasToolbar components exist
|
|
270
|
+
<div class="component-section">
|
|
271
|
+
<h3>Chat Panel (Organism)</h3>
|
|
272
|
+
<div style="height: 500px; border: 1px solid var(--border-default); border-radius: var(--radius-lg, 8px);">
|
|
273
|
+
<ChatPanel title="general-chat" memberCount={42}>
|
|
274
|
+
{#snippet headerActions()}<Button variant="ghost" size="sm"><Search size={16}/></Button>{/snippet}
|
|
275
|
+
{#snippet messages()}<p>Messages here</p>{/snippet}
|
|
276
|
+
{#snippet composer()}<Input placeholder="Message..." />{/snippet}
|
|
277
|
+
</ChatPanel>
|
|
278
|
+
</div>
|
|
279
|
+
</div>
|
|
280
|
+
|
|
281
|
+
<div class="component-section">
|
|
282
|
+
<h3>Canvas Toolbar</h3>
|
|
283
|
+
<div style="height: 200px; position: relative; background: var(--color-bg-canvas);">
|
|
284
|
+
<CanvasToolbar position="bottom">
|
|
285
|
+
<Button variant="ghost"><Settings size={16}/></Button>
|
|
286
|
+
</CanvasToolbar>
|
|
287
|
+
</div>
|
|
288
|
+
</div>
|
|
289
|
+
-->
|
|
290
|
+
</section>
|
|
291
|
+
{/if}
|
|
292
|
+
|
|
293
|
+
<!-- DATA VIZ -->
|
|
294
|
+
{#if activeTab === 'dataviz'}
|
|
295
|
+
<section class="ds-stack" style="gap: var(--space-8)">
|
|
296
|
+
<div class="component-section">
|
|
297
|
+
<h3>CheckpointBar — Timeline Navigation</h3>
|
|
298
|
+
<p style="color: var(--color-text-secondary); margin-bottom: 1rem;">
|
|
299
|
+
ADR: ui-timeline-checkpoint-bar-001 — Scrollable checkpoint navigation with color-coded components.
|
|
300
|
+
</p>
|
|
301
|
+
<div style="border: 1px solid var(--border-default); border-radius: var(--radius-lg, 8px); overflow: hidden;">
|
|
302
|
+
<CheckpointBar
|
|
303
|
+
checkpoints={demoCheckpoints}
|
|
304
|
+
currentSeqno={demoCurrentSeqno}
|
|
305
|
+
previewSeqno={demoPreviewSeqno}
|
|
306
|
+
onCheckpointClick={(cp) => { demoPreviewSeqno = cp.seqno; }}
|
|
307
|
+
onCheckpointDoubleClick={(cp) => { alert(`Rollback to checkpoint ${cp.seqno}?`); }}
|
|
308
|
+
/>
|
|
309
|
+
</div>
|
|
310
|
+
<div style="margin-top: 1rem; padding: 1rem; background: var(--color-bg-panel); border-radius: var(--radius-md);">
|
|
311
|
+
<p style="font-size: 0.85rem; color: var(--color-text-muted);">
|
|
312
|
+
<strong>Interactions:</strong> Hover for tooltip, Click to preview, Double-click to rollback, Arrow keys to navigate
|
|
313
|
+
</p>
|
|
314
|
+
<p style="font-size: 0.85rem; color: var(--color-text-muted); margin-top: 0.5rem;">
|
|
315
|
+
<strong>Current:</strong> {demoCurrentSeqno} |
|
|
316
|
+
<strong>Preview:</strong> {demoPreviewSeqno ?? 'None'} |
|
|
317
|
+
<button onclick={() => demoPreviewSeqno = null}>Exit Preview</button>
|
|
318
|
+
</p>
|
|
319
|
+
</div>
|
|
320
|
+
</div>
|
|
321
|
+
|
|
322
|
+
<div class="component-section">
|
|
323
|
+
<h3>Component Color Coding</h3>
|
|
324
|
+
<div class="demo-row">
|
|
325
|
+
<div style="display: flex; flex-direction: column; gap: 0.5rem;">
|
|
326
|
+
{#each Object.entries({ 'rust-crate': '#b87333', 'ui-bundle': '#5f9ea0', 'config': '#daa520', 'faculty-state': '#9370db', 'substrate-binary': '#cd5c5c', 'system-snapshot': '#2f4f4f' }) as [type, color]}
|
|
327
|
+
<div style="display: flex; align-items: center; gap: 0.75rem;">
|
|
328
|
+
<span style="color: {color}; font-size: 16px;">◉</span>
|
|
329
|
+
<span style="font-size: 0.85rem; color: var(--color-text-secondary); font-family: var(--font-mono);">{type}</span>
|
|
330
|
+
<span style="font-size: 0.75rem; color: var(--color-text-muted);">{color}</span>
|
|
331
|
+
</div>
|
|
332
|
+
{/each}
|
|
333
|
+
</div>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</section>
|
|
337
|
+
{/if}
|
|
338
|
+
|
|
339
|
+
</div>
|
|
340
|
+
</div>
|
|
341
|
+
|
|
342
|
+
<style>
|
|
343
|
+
.showcase {
|
|
344
|
+
padding: var(--space-8);
|
|
345
|
+
max-width: 1200px;
|
|
346
|
+
margin: 0 auto;
|
|
347
|
+
font-family: var(--font-sans);
|
|
348
|
+
color: var(--color-text-primary);
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
.showcase-header {
|
|
352
|
+
display: flex;
|
|
353
|
+
justify-content: space-between;
|
|
354
|
+
align-items: center;
|
|
355
|
+
margin-bottom: var(--space-6);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
h1 {
|
|
359
|
+
margin: 0;
|
|
360
|
+
font-size: var(--text-2xl);
|
|
361
|
+
background: linear-gradient(135deg, var(--color-accent) 0%, #E2A670 100%);
|
|
362
|
+
-webkit-background-clip: text;
|
|
363
|
+
-webkit-text-fill-color: transparent;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
h3 {
|
|
367
|
+
margin: 0 0 var(--space-4) 0;
|
|
368
|
+
font-weight: 500;
|
|
369
|
+
color: var(--color-text-secondary);
|
|
370
|
+
border-bottom: 1px solid var(--border-subtle);
|
|
371
|
+
padding-bottom: var(--space-2);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.showcase-content {
|
|
375
|
+
margin-top: var(--space-6);
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
.component-section {
|
|
379
|
+
display: flex;
|
|
380
|
+
flex-direction: column;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
.demo-row {
|
|
384
|
+
display: flex;
|
|
385
|
+
flex-wrap: wrap;
|
|
386
|
+
gap: var(--space-4);
|
|
387
|
+
align-items: flex-end;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
.ds-cluster {
|
|
391
|
+
display: flex;
|
|
392
|
+
align-items: center;
|
|
393
|
+
gap: var(--space-3);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
.glass-bg {
|
|
397
|
+
padding: var(--space-4);
|
|
398
|
+
background: linear-gradient(135deg, #FF6B6B, #4ECDC4);
|
|
399
|
+
border-radius: var(--radius-lg);
|
|
400
|
+
}
|
|
401
|
+
</style>
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// BspTilingCanvas — Amethyst/i3-style tiling with content-adaptive cards
|
|
3
|
+
// Cards size to their own content, snap together via BSP tree
|
|
4
|
+
|
|
5
|
+
import type { BlockMetrics } from '../x0-enchanted-blocks/core/block-layout.js';
|
|
6
|
+
|
|
7
|
+
interface TilingCard {
|
|
8
|
+
id: string;
|
|
9
|
+
metrics: BlockMetrics;
|
|
10
|
+
minWidth?: number;
|
|
11
|
+
minHeight?: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface Position {
|
|
15
|
+
id: string;
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
width: number;
|
|
19
|
+
height: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface Props {
|
|
23
|
+
cards: TilingCard[];
|
|
24
|
+
containerWidth: number;
|
|
25
|
+
containerHeight: number;
|
|
26
|
+
children: import('svelte').Snippet<[Position]>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
let {
|
|
30
|
+
cards,
|
|
31
|
+
containerWidth,
|
|
32
|
+
containerHeight,
|
|
33
|
+
children
|
|
34
|
+
}: Props = $props();
|
|
35
|
+
|
|
36
|
+
// ── BSP Layout Engine ──────────────────────────────────────────────────────
|
|
37
|
+
|
|
38
|
+
interface BspNode {
|
|
39
|
+
id: string;
|
|
40
|
+
cardId?: string;
|
|
41
|
+
x: number;
|
|
42
|
+
y: number;
|
|
43
|
+
width: number;
|
|
44
|
+
height: number;
|
|
45
|
+
split?: 'horizontal' | 'vertical';
|
|
46
|
+
ratio?: number;
|
|
47
|
+
first?: BspNode;
|
|
48
|
+
second?: BspNode;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Build BSP tree that fits cards like Amethyst/i3 tiling WM */
|
|
52
|
+
function buildBspLayout(
|
|
53
|
+
cards: TilingCard[],
|
|
54
|
+
containerW: number,
|
|
55
|
+
containerH: number
|
|
56
|
+
): BspNode[] {
|
|
57
|
+
if (cards.length === 0) return [];
|
|
58
|
+
|
|
59
|
+
// Sort cards by content area (largest first for better packing)
|
|
60
|
+
const sorted = [...cards].sort((a, b) => {
|
|
61
|
+
const areaA = a.metrics.idealWidth * a.metrics.height;
|
|
62
|
+
const areaB = b.metrics.idealWidth * b.metrics.height;
|
|
63
|
+
return areaB - areaA;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Build BSP tree recursively
|
|
67
|
+
function buildNode(
|
|
68
|
+
cardList: TilingCard[],
|
|
69
|
+
x: number,
|
|
70
|
+
y: number,
|
|
71
|
+
w: number,
|
|
72
|
+
h: number
|
|
73
|
+
): BspNode | undefined {
|
|
74
|
+
if (cardList.length === 0) return undefined;
|
|
75
|
+
|
|
76
|
+
const [first, ...rest] = cardList;
|
|
77
|
+
|
|
78
|
+
if (rest.length === 0) {
|
|
79
|
+
// Leaf node - single card fills remaining space
|
|
80
|
+
return {
|
|
81
|
+
id: `leaf-${first.id}`,
|
|
82
|
+
cardId: first.id,
|
|
83
|
+
x, y,
|
|
84
|
+
width: w,
|
|
85
|
+
height: h,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Internal node - split space
|
|
90
|
+
const split = w > h ? 'vertical' : 'horizontal';
|
|
91
|
+
const dividerThickness = 6;
|
|
92
|
+
|
|
93
|
+
// Calculate ratio based on first card's preferred size vs remaining space
|
|
94
|
+
let ratio: number;
|
|
95
|
+
if (split === 'vertical') {
|
|
96
|
+
const idealW = Math.min(first.metrics.idealWidth + 24, w * 0.6);
|
|
97
|
+
ratio = idealW / (w - dividerThickness);
|
|
98
|
+
} else {
|
|
99
|
+
const idealH = Math.min(first.metrics.height + 24, h * 0.6);
|
|
100
|
+
ratio = idealH / (h - dividerThickness);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Clamp ratio
|
|
104
|
+
ratio = Math.max(0.2, Math.min(0.8, ratio));
|
|
105
|
+
|
|
106
|
+
let firstRect, secondRect;
|
|
107
|
+
if (split === 'vertical') {
|
|
108
|
+
const splitX = x + (w - dividerThickness) * ratio;
|
|
109
|
+
firstRect = { x, y, w: splitX - x, h };
|
|
110
|
+
secondRect = { x: splitX + dividerThickness, y, w: x + w - splitX - dividerThickness, h };
|
|
111
|
+
} else {
|
|
112
|
+
const splitY = y + (h - dividerThickness) * ratio;
|
|
113
|
+
firstRect = { x, y, w, h: splitY - y };
|
|
114
|
+
secondRect = { x, y: splitY + dividerThickness, w, h: y + h - splitY - dividerThickness };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
id: `split-${first.id}`,
|
|
119
|
+
x, y, width: w, height: h,
|
|
120
|
+
split, ratio,
|
|
121
|
+
first: buildNode([first], firstRect.x, firstRect.y, firstRect.w, firstRect.h),
|
|
122
|
+
second: buildNode(rest, secondRect.x, secondRect.y, secondRect.w, secondRect.h),
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const root = buildNode(sorted, 0, 0, containerW, containerH);
|
|
127
|
+
return root ? flattenBsp(root) : [];
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Flatten BSP tree to card positions */
|
|
131
|
+
function flattenBsp(node: BspNode): BspNode[] {
|
|
132
|
+
if (node.cardId) {
|
|
133
|
+
return [node];
|
|
134
|
+
}
|
|
135
|
+
const results: BspNode[] = [];
|
|
136
|
+
if (node.first) results.push(...flattenBsp(node.first));
|
|
137
|
+
if (node.second) results.push(...flattenBsp(node.second));
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// ── Derived Layout ──────────────────────────────────────────────────────────
|
|
142
|
+
const layout = $derived(buildBspLayout(cards, containerWidth, containerHeight));
|
|
143
|
+
|
|
144
|
+
function getPosition(id: string) {
|
|
145
|
+
const node = layout.find(n => n.cardId === id);
|
|
146
|
+
if (!node) return { x: 0, y: 0, width: 200, height: 100 };
|
|
147
|
+
return { x: node.x, y: node.y, width: node.width, height: node.height };
|
|
148
|
+
}
|
|
149
|
+
</script>
|
|
150
|
+
|
|
151
|
+
<div class="bsp-tiling-canvas" style:width="{containerWidth}px" style:height="{containerHeight}px">
|
|
152
|
+
{#each cards as card (card.id)}
|
|
153
|
+
{@const pos = getPosition(card.id)}
|
|
154
|
+
<div
|
|
155
|
+
class="tiling-slot"
|
|
156
|
+
style:left="{pos.x}px"
|
|
157
|
+
style:top="{pos.y}px"
|
|
158
|
+
style:width="{pos.width}px"
|
|
159
|
+
style:height="{pos.height}px"
|
|
160
|
+
>
|
|
161
|
+
{@render children({ id: card.id, ...pos })}
|
|
162
|
+
</div>
|
|
163
|
+
{/each}
|
|
164
|
+
|
|
165
|
+
<!-- Divider lines (visual only) -->
|
|
166
|
+
{#each layout as node, i}
|
|
167
|
+
{#if i < layout.length - 1}
|
|
168
|
+
{@const next = layout[i + 1]}
|
|
169
|
+
{#if Math.abs(node.x + node.width - next.x) < 3}
|
|
170
|
+
<!-- Vertical divider -->
|
|
171
|
+
<div
|
|
172
|
+
class="divider vertical"
|
|
173
|
+
style:left="{node.x + node.width}px"
|
|
174
|
+
style:top="{Math.min(node.y, next.y)}px"
|
|
175
|
+
style:height="{Math.max(node.y + node.height, next.y + next.height) - Math.min(node.y, next.y)}px"
|
|
176
|
+
></div>
|
|
177
|
+
{:else if Math.abs(node.y + node.height - next.y) < 3}
|
|
178
|
+
<!-- Horizontal divider -->
|
|
179
|
+
<div
|
|
180
|
+
class="divider horizontal"
|
|
181
|
+
style:left="{Math.min(node.x, next.x)}px"
|
|
182
|
+
style:top="{node.y + node.height}px"
|
|
183
|
+
style:width="{Math.max(node.x + node.width, next.x + next.width) - Math.min(node.x, next.x)}px"
|
|
184
|
+
></div>
|
|
185
|
+
{/if}
|
|
186
|
+
{/if}
|
|
187
|
+
{/each}
|
|
188
|
+
</div>
|
|
189
|
+
|
|
190
|
+
<style>
|
|
191
|
+
.bsp-tiling-canvas {
|
|
192
|
+
position: relative;
|
|
193
|
+
overflow: hidden;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.tiling-slot {
|
|
197
|
+
position: absolute;
|
|
198
|
+
overflow: auto;
|
|
199
|
+
/* Subtle border for card separation */
|
|
200
|
+
box-shadow: inset 0 0 0 1px var(--color-border, #333);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.tiling-slot::-webkit-scrollbar {
|
|
204
|
+
width: 4px;
|
|
205
|
+
height: 4px;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.tiling-slot::-webkit-scrollbar-thumb {
|
|
209
|
+
background: var(--color-text-muted, #706858);
|
|
210
|
+
border-radius: 2px;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.divider {
|
|
214
|
+
position: absolute;
|
|
215
|
+
background: var(--color-border, #333);
|
|
216
|
+
z-index: 10;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.divider.vertical {
|
|
220
|
+
width: 6px;
|
|
221
|
+
cursor: col-resize;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.divider.horizontal {
|
|
225
|
+
height: 6px;
|
|
226
|
+
cursor: row-resize;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.divider:hover {
|
|
230
|
+
background: var(--color-accent, #b87333);
|
|
231
|
+
}
|
|
232
|
+
</style>
|