@keyfunc/keybricks-ds 1.0.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/DEPLOY.md +204 -0
- package/DESIGN.md +269 -0
- package/INTEGRATION.md +417 -0
- package/SKILL.md +32 -0
- package/_adherence.oxlintrc.json +1469 -0
- package/_scripts/build-index.mjs +80 -0
- package/_scripts/build-tokens.mjs +135 -0
- package/assets/icons/icon-index.js +1 -0
- package/assets/icons/icon-index.json +1 -0
- package/assets/icons/unicons-index.js +1 -0
- package/assets/icons/unicons-index.json +1 -0
- package/assets/keybricks-logo.png +0 -0
- package/components/_cards/accordion.card.html +32 -0
- package/components/_cards/action-chip.card.html +22 -0
- package/components/_cards/activity.card.html +34 -0
- package/components/_cards/alert.card.html +32 -0
- package/components/_cards/app-chrome.card.html +36 -0
- package/components/_cards/banner.card.html +21 -0
- package/components/_cards/brand-visual.card.html +43 -0
- package/components/_cards/breadcrumb.card.html +23 -0
- package/components/_cards/button-1-base.card.html +39 -0
- package/components/_cards/button-2-states.card.html +56 -0
- package/components/_cards/button-3-sizes-icons.card.html +32 -0
- package/components/_cards/button-4-group.card.html +47 -0
- package/components/_cards/canvas-zoom-controls.card.html +19 -0
- package/components/_cards/cards-indicators.card.html +59 -0
- package/components/_cards/charts-1-sparkline.card.html +55 -0
- package/components/_cards/charts-10-gauge.card.html +31 -0
- package/components/_cards/charts-11-funnel.card.html +28 -0
- package/components/_cards/charts-12-treemap.card.html +23 -0
- package/components/_cards/charts-14-radar.card.html +31 -0
- package/components/_cards/charts-15-distribution.card.html +32 -0
- package/components/_cards/charts-16-pareto.card.html +25 -0
- package/components/_cards/charts-17-candlestick.card.html +34 -0
- package/components/_cards/charts-18-sankey.card.html +29 -0
- package/components/_cards/charts-19-chord.card.html +31 -0
- package/components/_cards/charts-2-line.card.html +46 -0
- package/components/_cards/charts-20-sunburst.card.html +38 -0
- package/components/_cards/charts-21-worldmap.card.html +33 -0
- package/components/_cards/charts-22-gantt.card.html +37 -0
- package/components/_cards/charts-23-bar3d.card.html +26 -0
- package/components/_cards/charts-24-pie3d.card.html +32 -0
- package/components/_cards/charts-25-surface3d.card.html +24 -0
- package/components/_cards/charts-26-scatter3d.card.html +24 -0
- package/components/_cards/charts-27-ribbon3d.card.html +23 -0
- package/components/_cards/charts-3-bar.card.html +71 -0
- package/components/_cards/charts-4-barlist.card.html +30 -0
- package/components/_cards/charts-5-donut.card.html +35 -0
- package/components/_cards/charts-6-scatter.card.html +26 -0
- package/components/_cards/charts-7-heatmap.card.html +28 -0
- package/components/_cards/charts-8-waterfall.card.html +25 -0
- package/components/_cards/charts-9-bullet.card.html +31 -0
- package/components/_cards/charts-99-fullpage.card.html +194 -0
- package/components/_cards/checkbox.card.html +34 -0
- package/components/_cards/codeblock.card.html +19 -0
- package/components/_cards/combobox.card.html +25 -0
- package/components/_cards/context-menu.card.html +21 -0
- package/components/_cards/datatable.card.html +48 -0
- package/components/_cards/date-picker.card.html +31 -0
- package/components/_cards/diffvalue.card.html +21 -0
- package/components/_cards/divider.card.html +24 -0
- package/components/_cards/drawer.card.html +30 -0
- package/components/_cards/dropdown-menu.card.html +38 -0
- package/components/_cards/dropzone.card.html +22 -0
- package/components/_cards/empty-state.card.html +29 -0
- package/components/_cards/filter-bar.card.html +34 -0
- package/components/_cards/icon-button.card.html +27 -0
- package/components/_cards/icons-1-core.card.html +49 -0
- package/components/_cards/icons-2-feather.card.html +39 -0
- package/components/_cards/icons-3-unicons.card.html +44 -0
- package/components/_cards/input.card.html +25 -0
- package/components/_cards/kbd.card.html +20 -0
- package/components/_cards/keyvalue.card.html +23 -0
- package/components/_cards/listrow.card.html +30 -0
- package/components/_cards/loading.card.html +33 -0
- package/components/_cards/log-viewer.card.html +23 -0
- package/components/_cards/modal.card.html +35 -0
- package/components/_cards/multi-select.card.html +26 -0
- package/components/_cards/number-stepper.card.html +24 -0
- package/components/_cards/pagination.card.html +25 -0
- package/components/_cards/people.card.html +46 -0
- package/components/_cards/pipeline-node.card.html +76 -0
- package/components/_cards/pipeline-palette.card.html +54 -0
- package/components/_cards/search.card.html +23 -0
- package/components/_cards/select.card.html +23 -0
- package/components/_cards/slider.card.html +25 -0
- package/components/_cards/smart-table.card.html +37 -0
- package/components/_cards/spreadsheet.card.html +31 -0
- package/components/_cards/stepper.card.html +25 -0
- package/components/_cards/tabs.card.html +25 -0
- package/components/_cards/tag.card.html +26 -0
- package/components/_cards/textarea.card.html +27 -0
- package/components/_cards/timeline.card.html +32 -0
- package/components/_cards/toast.card.html +33 -0
- package/components/_cards/toggles.card.html +68 -0
- package/components/_cards/tooltip.card.html +22 -0
- package/components/_cards/tree.card.html +150 -0
- package/components/_shared/frame-theme.js +16 -0
- package/components/_shared/src-brand.js +1 -0
- package/components/_shared/src-buttons.js +1 -0
- package/components/_shared/src-charts.js +1 -0
- package/components/_shared/src-data.js +1 -0
- package/components/_shared/src-display.js +1 -0
- package/components/_shared/src-feedback.js +1 -0
- package/components/_shared/src-forms.js +1 -0
- package/components/_shared/src-icons.js +1 -0
- package/components/_shared/src-navigation.js +1 -0
- package/components/_shared/src-pipeline.js +1 -0
- package/components/_shared/src-tabs.js +1 -0
- package/components/_shared/svg-download.js +19 -0
- package/components/_shared/view-source.js +185 -0
- package/components/_vue/index.mjs +114 -0
- package/components/brand/BrandVisual.d.ts +17 -0
- package/components/brand/BrandVisual.jsx +29 -0
- package/components/brand/BrandVisual.vue +34 -0
- package/components/brand/_dir.card.html +13 -0
- package/components/brand/brandMotifs.js +210 -0
- package/components/buttons/ActionChip.d.ts +10 -0
- package/components/buttons/ActionChip.jsx +20 -0
- package/components/buttons/ActionChip.vue +18 -0
- package/components/buttons/Button.d.ts +40 -0
- package/components/buttons/Button.jsx +101 -0
- package/components/buttons/Button.vue +87 -0
- package/components/buttons/IconButton.d.ts +26 -0
- package/components/buttons/IconButton.jsx +28 -0
- package/components/buttons/IconButton.vue +21 -0
- package/components/buttons/_dir.card.html +13 -0
- package/components/buttons/buttons.prompt.md +19 -0
- package/components/charts/Bar3D.d.ts +15 -0
- package/components/charts/Bar3D.jsx +93 -0
- package/components/charts/Bar3D.vue +79 -0
- package/components/charts/BarChart.d.ts +23 -0
- package/components/charts/BarChart.jsx +135 -0
- package/components/charts/BarChart.vue +139 -0
- package/components/charts/BarList.d.ts +13 -0
- package/components/charts/BarList.jsx +31 -0
- package/components/charts/BarList.vue +25 -0
- package/components/charts/BoxPlot.d.ts +18 -0
- package/components/charts/BoxPlot.jsx +62 -0
- package/components/charts/BoxPlot.vue +58 -0
- package/components/charts/BulletChart.d.ts +12 -0
- package/components/charts/BulletChart.jsx +41 -0
- package/components/charts/BulletChart.vue +33 -0
- package/components/charts/CandlestickChart.d.ts +15 -0
- package/components/charts/CandlestickChart.jsx +63 -0
- package/components/charts/CandlestickChart.vue +62 -0
- package/components/charts/ChartKit.d.ts +8 -0
- package/components/charts/ChartKit.jsx +76 -0
- package/components/charts/ChartKit.vue +47 -0
- package/components/charts/ChordChart.d.ts +13 -0
- package/components/charts/ChordChart.jsx +55 -0
- package/components/charts/ChordChart.vue +54 -0
- package/components/charts/DonutChart.d.ts +15 -0
- package/components/charts/DonutChart.jsx +73 -0
- package/components/charts/DonutChart.vue +61 -0
- package/components/charts/FunnelChart.d.ts +11 -0
- package/components/charts/FunnelChart.jsx +37 -0
- package/components/charts/FunnelChart.vue +29 -0
- package/components/charts/GanttChart.d.ts +20 -0
- package/components/charts/GanttChart.jsx +80 -0
- package/components/charts/GanttChart.vue +60 -0
- package/components/charts/GaugeChart.d.ts +13 -0
- package/components/charts/GaugeChart.jsx +33 -0
- package/components/charts/GaugeChart.vue +29 -0
- package/components/charts/HeatmapChart.d.ts +13 -0
- package/components/charts/HeatmapChart.jsx +62 -0
- package/components/charts/HeatmapChart.vue +38 -0
- package/components/charts/Histogram.d.ts +16 -0
- package/components/charts/Histogram.jsx +56 -0
- package/components/charts/Histogram.vue +53 -0
- package/components/charts/LineChart.d.ts +17 -0
- package/components/charts/LineChart.jsx +84 -0
- package/components/charts/LineChart.vue +86 -0
- package/components/charts/ParetoChart.d.ts +13 -0
- package/components/charts/ParetoChart.jsx +54 -0
- package/components/charts/ParetoChart.vue +55 -0
- package/components/charts/Pie3D.d.ts +17 -0
- package/components/charts/Pie3D.jsx +145 -0
- package/components/charts/Pie3D.vue +115 -0
- package/components/charts/RadarChart.d.ts +14 -0
- package/components/charts/RadarChart.jsx +79 -0
- package/components/charts/RadarChart.vue +73 -0
- package/components/charts/Ribbon3D.d.ts +13 -0
- package/components/charts/Ribbon3D.jsx +89 -0
- package/components/charts/Ribbon3D.vue +79 -0
- package/components/charts/SankeyChart.d.ts +15 -0
- package/components/charts/SankeyChart.jsx +59 -0
- package/components/charts/SankeyChart.vue +53 -0
- package/components/charts/Scatter3D.d.ts +12 -0
- package/components/charts/Scatter3D.jsx +69 -0
- package/components/charts/Scatter3D.vue +59 -0
- package/components/charts/ScatterChart.d.ts +12 -0
- package/components/charts/ScatterChart.jsx +53 -0
- package/components/charts/ScatterChart.vue +57 -0
- package/components/charts/Sparkline.d.ts +19 -0
- package/components/charts/Sparkline.jsx +63 -0
- package/components/charts/Sparkline.vue +54 -0
- package/components/charts/SunburstChart.d.ts +12 -0
- package/components/charts/SunburstChart.jsx +61 -0
- package/components/charts/SunburstChart.vue +60 -0
- package/components/charts/Surface3D.d.ts +13 -0
- package/components/charts/Surface3D.jsx +70 -0
- package/components/charts/Surface3D.vue +63 -0
- package/components/charts/TreemapChart.d.ts +11 -0
- package/components/charts/TreemapChart.jsx +35 -0
- package/components/charts/TreemapChart.vue +27 -0
- package/components/charts/WaterfallChart.d.ts +11 -0
- package/components/charts/WaterfallChart.jsx +59 -0
- package/components/charts/WaterfallChart.vue +46 -0
- package/components/charts/WorldMap.d.ts +21 -0
- package/components/charts/WorldMap.jsx +107 -0
- package/components/charts/WorldMap.vue +73 -0
- package/components/charts/_dir.card.html +7 -0
- package/components/charts/chart-3d.js +84 -0
- package/components/charts/chart-geometry.js +223 -0
- package/components/charts/charts.prompt.md +24 -0
- package/components/data/CodeBlock.d.ts +9 -0
- package/components/data/CodeBlock.jsx +19 -0
- package/components/data/CodeBlock.vue +22 -0
- package/components/data/DataTable.d.ts +28 -0
- package/components/data/DataTable.jsx +32 -0
- package/components/data/DataTable.vue +25 -0
- package/components/data/DiffValue.d.ts +11 -0
- package/components/data/DiffValue.jsx +15 -0
- package/components/data/DiffValue.vue +13 -0
- package/components/data/FilterBar.d.ts +16 -0
- package/components/data/FilterBar.jsx +22 -0
- package/components/data/FilterBar.vue +23 -0
- package/components/data/KeyValue.d.ts +16 -0
- package/components/data/KeyValue.jsx +25 -0
- package/components/data/KeyValue.vue +26 -0
- package/components/data/ListRow.d.ts +14 -0
- package/components/data/ListRow.jsx +25 -0
- package/components/data/ListRow.vue +25 -0
- package/components/data/Pagination.d.ts +12 -0
- package/components/data/Pagination.jsx +27 -0
- package/components/data/Pagination.vue +24 -0
- package/components/data/SmartTable.d.ts +30 -0
- package/components/data/SmartTable.jsx +73 -0
- package/components/data/SmartTable.vue +74 -0
- package/components/data/Spreadsheet.d.ts +33 -0
- package/components/data/Spreadsheet.jsx +110 -0
- package/components/data/Spreadsheet.vue +95 -0
- package/components/data/_dir.card.html +13 -0
- package/components/data/data.prompt.md +33 -0
- package/components/display/Avatar.d.ts +21 -0
- package/components/display/Avatar.jsx +31 -0
- package/components/display/Avatar.vue +34 -0
- package/components/display/Badge.d.ts +27 -0
- package/components/display/Badge.jsx +40 -0
- package/components/display/Badge.vue +47 -0
- package/components/display/Card.d.ts +27 -0
- package/components/display/Card.jsx +27 -0
- package/components/display/Card.vue +28 -0
- package/components/display/Divider.d.ts +8 -0
- package/components/display/Divider.jsx +14 -0
- package/components/display/Divider.vue +13 -0
- package/components/display/Kbd.d.ts +7 -0
- package/components/display/Kbd.jsx +12 -0
- package/components/display/Kbd.vue +9 -0
- package/components/display/ProgressBar.d.ts +42 -0
- package/components/display/ProgressBar.jsx +70 -0
- package/components/display/ProgressBar.vue +69 -0
- package/components/display/Tag.d.ts +9 -0
- package/components/display/Tag.jsx +15 -0
- package/components/display/Tag.vue +15 -0
- package/components/display/_dir.card.html +13 -0
- package/components/display/display.prompt.md +30 -0
- package/components/feedback/Accordion.d.ts +13 -0
- package/components/feedback/Accordion.jsx +21 -0
- package/components/feedback/Accordion.vue +22 -0
- package/components/feedback/ActivityItem.d.ts +25 -0
- package/components/feedback/ActivityItem.jsx +39 -0
- package/components/feedback/ActivityItem.vue +46 -0
- package/components/feedback/Alert.d.ts +36 -0
- package/components/feedback/Alert.jsx +48 -0
- package/components/feedback/Alert.vue +59 -0
- package/components/feedback/Banner.d.ts +15 -0
- package/components/feedback/Banner.jsx +25 -0
- package/components/feedback/Banner.vue +25 -0
- package/components/feedback/ContextMenu.d.ts +8 -0
- package/components/feedback/ContextMenu.jsx +18 -0
- package/components/feedback/ContextMenu.vue +19 -0
- package/components/feedback/Drawer.d.ts +18 -0
- package/components/feedback/Drawer.jsx +26 -0
- package/components/feedback/Drawer.vue +24 -0
- package/components/feedback/Menu.d.ts +10 -0
- package/components/feedback/Menu.jsx +24 -0
- package/components/feedback/Menu.vue +21 -0
- package/components/feedback/Modal.d.ts +38 -0
- package/components/feedback/Modal.jsx +69 -0
- package/components/feedback/Modal.vue +65 -0
- package/components/feedback/Stepper.d.ts +9 -0
- package/components/feedback/Stepper.jsx +24 -0
- package/components/feedback/Stepper.vue +18 -0
- package/components/feedback/Timeline.d.ts +15 -0
- package/components/feedback/Timeline.jsx +27 -0
- package/components/feedback/Timeline.vue +21 -0
- package/components/feedback/_dir.card.html +13 -0
- package/components/feedback/feedback.prompt.md +35 -0
- package/components/forms/Checkbox.d.ts +13 -0
- package/components/forms/Checkbox.jsx +17 -0
- package/components/forms/Checkbox.vue +18 -0
- package/components/forms/Combobox.d.ts +13 -0
- package/components/forms/Combobox.jsx +34 -0
- package/components/forms/Combobox.vue +35 -0
- package/components/forms/DatePicker.d.ts +12 -0
- package/components/forms/DatePicker.jsx +48 -0
- package/components/forms/DatePicker.vue +46 -0
- package/components/forms/Dropzone.d.ts +20 -0
- package/components/forms/Dropzone.jsx +36 -0
- package/components/forms/Dropzone.vue +28 -0
- package/components/forms/Field.d.ts +13 -0
- package/components/forms/Field.jsx +13 -0
- package/components/forms/Field.vue +12 -0
- package/components/forms/Input.d.ts +18 -0
- package/components/forms/Input.jsx +35 -0
- package/components/forms/Input.vue +38 -0
- package/components/forms/MultiSelect.d.ts +14 -0
- package/components/forms/MultiSelect.jsx +33 -0
- package/components/forms/MultiSelect.vue +36 -0
- package/components/forms/NumberStepper.d.ts +13 -0
- package/components/forms/NumberStepper.jsx +15 -0
- package/components/forms/NumberStepper.vue +17 -0
- package/components/forms/Radio.vue +16 -0
- package/components/forms/RadioGroup.vue +11 -0
- package/components/forms/SearchInput.d.ts +12 -0
- package/components/forms/SearchInput.jsx +17 -0
- package/components/forms/SearchInput.vue +14 -0
- package/components/forms/Select.d.ts +14 -0
- package/components/forms/Select.jsx +17 -0
- package/components/forms/Select.vue +16 -0
- package/components/forms/Slider.d.ts +15 -0
- package/components/forms/Slider.jsx +21 -0
- package/components/forms/Slider.vue +23 -0
- package/components/forms/Switch.d.ts +31 -0
- package/components/forms/Switch.jsx +40 -0
- package/components/forms/Switch.vue +16 -0
- package/components/forms/_dir.card.html +13 -0
- package/components/forms/forms.prompt.md +23 -0
- package/components/icons/Icon.d.ts +19 -0
- package/components/icons/Icon.jsx +93 -0
- package/components/icons/Icon.prompt.md +8 -0
- package/components/icons/Icon.vue +12 -0
- package/components/icons/Unicon.d.ts +12 -0
- package/components/icons/Unicon.jsx +16 -0
- package/components/icons/Unicon.vue +15 -0
- package/components/icons/_dir.card.html +13 -0
- package/components/icons/feather-paths.js +2 -0
- package/components/icons/icon-paths.js +73 -0
- package/components/index.d.ts +87 -0
- package/components/index.js +110 -0
- package/components/navigation/Breadcrumb.d.ts +24 -0
- package/components/navigation/Breadcrumb.jsx +120 -0
- package/components/navigation/Breadcrumb.vue +94 -0
- package/components/navigation/DocTree.d.ts +18 -0
- package/components/navigation/DocTree.jsx +37 -0
- package/components/navigation/DocTree.vue +25 -0
- package/components/navigation/Sidebar.d.ts +29 -0
- package/components/navigation/Sidebar.jsx +75 -0
- package/components/navigation/Sidebar.vue +54 -0
- package/components/navigation/TopBar.d.ts +27 -0
- package/components/navigation/TopBar.jsx +44 -0
- package/components/navigation/TopBar.vue +51 -0
- package/components/navigation/Tree.d.ts +83 -0
- package/components/navigation/Tree.jsx +347 -0
- package/components/navigation/Tree.vue +258 -0
- package/components/navigation/_dir.card.html +13 -0
- package/components/navigation/navigation.prompt.md +22 -0
- package/components/pipeline/ConfigPanel.d.ts +18 -0
- package/components/pipeline/ConfigPanel.jsx +30 -0
- package/components/pipeline/ConfigPanel.vue +32 -0
- package/components/pipeline/DataPreview.d.ts +14 -0
- package/components/pipeline/DataPreview.jsx +24 -0
- package/components/pipeline/DataPreview.vue +21 -0
- package/components/pipeline/NodeCard.d.ts +47 -0
- package/components/pipeline/NodeCard.jsx +97 -0
- package/components/pipeline/NodeCard.vue +75 -0
- package/components/pipeline/PaletteItem.vue +15 -0
- package/components/pipeline/PaletteListItem.vue +15 -0
- package/components/pipeline/PalettePanel.d.ts +20 -0
- package/components/pipeline/PalettePanel.jsx +74 -0
- package/components/pipeline/PalettePanel.vue +45 -0
- package/components/pipeline/PipelineCanvas.d.ts +25 -0
- package/components/pipeline/PipelineCanvas.jsx +94 -0
- package/components/pipeline/PipelineCanvas.vue +73 -0
- package/components/pipeline/ZoomControls.d.ts +9 -0
- package/components/pipeline/ZoomControls.jsx +15 -0
- package/components/pipeline/ZoomControls.vue +15 -0
- package/components/pipeline/_dir.card.html +13 -0
- package/components/pipeline/pipeline.prompt.md +22 -0
- package/components/tabs/SegmentedControl.d.ts +11 -0
- package/components/tabs/SegmentedControl.jsx +21 -0
- package/components/tabs/SegmentedControl.vue +15 -0
- package/components/tabs/Tabs.d.ts +13 -0
- package/components/tabs/Tabs.jsx +52 -0
- package/components/tabs/Tabs.vue +39 -0
- package/components/tabs/ViewToggle.d.ts +8 -0
- package/components/tabs/ViewToggle.jsx +12 -0
- package/components/tabs/ViewToggle.vue +12 -0
- package/components/tabs/_dir.card.html +13 -0
- package/components/tabs/tabs.prompt.md +14 -0
- package/package.json +76 -0
- package/readme.md +150 -0
- package/styles.css +10 -0
- package/tokens/alpha.css +27 -0
- package/tokens/charts.css +55 -0
- package/tokens/colors-extended.css +40 -0
- package/tokens/colors.css +39 -0
- package/tokens/dark.css +96 -0
- package/tokens/fonts.css +4 -0
- package/tokens/interaction.css +48 -0
- package/tokens/layout.css +30 -0
- package/tokens/motion.css +22 -0
- package/tokens/tokens.d.ts +680 -0
- package/tokens/tokens.js +1018 -0
- package/tokens/tokens.json +2928 -0
- package/tokens/typography.css +22 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.__KB_SRC__=Object.assign(window.__KB_SRC__||{},{"pipeline/ConfigPanel.jsx":"import React from \"react\";\nimport { Button } from \"../buttons/Button.jsx\";\nimport { PanelTabs } from \"../tabs/Tabs.jsx\";\nimport { NumberStepper } from \"../forms/NumberStepper.jsx\";\n\n/** Right node-config panel — 400px: title/subtitle + Save Settings, preview-rows row, PanelTabs, body slot. */\nexport function ConfigPanel({ title, subtitle, tabs = [\"Node Info\", \"Calc Rules\", \"Output Fields\", \"SQL Preview\"], activeTab, onTab, previewRows = 100, onPreviewRows, onSave, onPreview, children, height = \"100%\", style }) {\n const [tab, setTab] = React.useState(tabs[0]);\n const cur = activeTab != null ? activeTab : tab;\n return (\n <div style={{ width: \"var(--panel-w)\", height, background: \"var(--bg-surface)\", borderLeft: \"1px solid var(--border-card)\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\", flexShrink: 0, ...style }}>\n <div style={{ padding: \"12px 16px 0\", borderBottom: \"1px solid var(--border-card)\", flexShrink: 0 }}>\n <div style={{ display: \"flex\", justifyContent: \"space-between\", alignItems: \"flex-start\" }}>\n <div style={{ display: \"flex\", flexDirection: \"column\" }}>\n <span style={{ fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-semibold)\", fontSize: \"var(--text-md)\", lineHeight: \"17.5px\", letterSpacing: \"var(--tracking-md)\", color: \"var(--text-heading)\" }}>{title}</span>\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-xs)\", lineHeight: \"var(--leading-xs)\", letterSpacing: \"var(--tracking-xs)\", color: \"var(--text-muted)\", paddingTop: 5 }}>{subtitle}</span>\n </div>\n <Button size=\"sm\" onClick={onSave}>Save Settings</Button>\n </div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, padding: \"8px 0\" }}>\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-sm)\", lineHeight: \"var(--leading-sm)\", color: \"var(--text-secondary)\" }}>Preview rows</span>\n <NumberStepper value={previewRows} onChange={onPreviewRows} />\n <Button variant=\"primary-soft\" size=\"sm\" icon=\"eye\" style={{ height: 28, borderRadius: \"var(--radius-sm)\", padding: \"4px 10px\" }} onClick={onPreview}>Data Preview</Button>\n </div>\n <PanelTabs tabs={tabs} active={cur} onChange={(t) => { setTab(t); onTab && onTab(t); }} style={{ margin: \"0 -16px\", padding: \"0 16px\", borderBottom: \"none\" }} />\n </div>\n <div style={{ flex: 1, overflowY: \"auto\", padding: 16, display: \"flex\", flexDirection: \"column\", gap: 16 }}>{children}</div>\n </div>\n );\n}\n","pipeline/ConfigPanel.vue":"<script setup>\nimport { ref, computed } from 'vue'\nimport Button from '../buttons/Button.vue'\nimport PanelTabs from '../tabs/Tabs.vue'\nimport NumberStepper from '../forms/NumberStepper.vue'\n// Right node-config panel — 400px: title/subtitle + Save Settings, preview-rows row, PanelTabs, body slot.\nconst props = defineProps({ title: String, subtitle: String, tabs: { type: Array, default: () => ['Node Info', 'Calc Rules', 'Output Fields', 'SQL Preview'] }, activeTab: String, previewRows: { type: Number, default: 100 }, height: { type: [String, Number], default: '100%' }, style: Object })\nconst emit = defineEmits(['tab', 'update:previewRows', 'save', 'preview'])\nconst tabState = ref(props.tabs[0])\nconst cur = computed(() => props.activeTab != null ? props.activeTab : tabState.value)\nfunction setTab(t) { tabState.value = t; emit('tab', t) }\n</script>\n<template>\n <div :style=\"{ width: 'var(--panel-w)', height, background: 'var(--bg-surface)', borderLeft: '1px solid var(--border-card)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', flexShrink: 0, ...style }\">\n <div style=\"padding:12px 16px 0;border-bottom:1px solid var(--border-card);flex-shrink:0\">\n <div style=\"display:flex;justify-content:space-between;align-items:flex-start\">\n <div style=\"display:flex;flex-direction:column\">\n <span style=\"font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-md);line-height:17.5px;letter-spacing:var(--tracking-md);color:var(--text-heading)\">{{ title }}</span>\n <span style=\"font-family:var(--font-sans);font-size:var(--text-xs);line-height:var(--leading-xs);letter-spacing:var(--tracking-xs);color:var(--text-muted);padding-top:5px\">{{ subtitle }}</span>\n </div>\n <Button size=\"sm\" @click=\"emit('save')\">Save Settings</Button>\n </div>\n <div style=\"display:flex;align-items:center;gap:8px;padding:8px 0\">\n <span style=\"font-family:var(--font-sans);font-size:var(--text-sm);line-height:var(--leading-sm);color:var(--text-secondary)\">Preview rows</span>\n <NumberStepper :model-value=\"previewRows\" @update:modelValue=\"v => emit('update:previewRows', v)\" />\n <Button variant=\"primary-soft\" size=\"sm\" icon=\"eye\" style=\"height:28px;border-radius:var(--radius-sm);padding:4px 10px\" @click=\"emit('preview')\">Data Preview</Button>\n </div>\n <PanelTabs :tabs=\"tabs\" :active=\"cur\" @change=\"setTab\" style=\"margin:0 -16px;padding:0 16px;border-bottom:none\" />\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:16px\"><slot /></div>\n </div>\n</template>\n","pipeline/DataPreview.jsx":"import React from \"react\";\nimport { Icon } from \"../icons/Icon.jsx\";\nimport { IconButton } from \"../buttons/IconButton.jsx\";\nimport { Checkbox } from \"../forms/Checkbox.jsx\";\nimport { DataTable } from \"../data/DataTable.jsx\";\n\n/** Bottom data-preview drawer — 250px: 40px header (title, Debug Mode, collapse) + dense table. */\nexport function DataPreview({ title = \"Data Preview\", columns = [], rows = [], debug = false, onDebug, collapsed, onCollapse, style }) {\n return (\n <div style={{ background: \"var(--bg-surface)\", borderTop: \"1px solid var(--border-card)\", display: \"flex\", flexDirection: \"column\", height: collapsed ? 40 : \"var(--drawer-h)\", overflow: \"hidden\", boxSizing: \"border-box\", flexShrink: 0, transition: \"height .15s ease\", ...style }}>\n <div style={{ height: 40, display: \"flex\", alignItems: \"center\", gap: 12, padding: \"0 16px\", boxSizing: \"border-box\", flexShrink: 0 }}>\n <span style={{ fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-semibold)\", fontSize: \"var(--text-base)\", lineHeight: \"var(--leading-base)\", letterSpacing: \"var(--tracking-base)\", color: \"var(--text-heading)\" }}>{title}</span>\n <span style={{ flex: 1 }} />\n <Checkbox label=\"Debug Mode\" checked={debug} onChange={onDebug} />\n <IconButton icon={collapsed ? \"chevron-up\" : \"chevron-down\"} size=\"xs\" muted onClick={onCollapse} />\n </div>\n {!collapsed ? (\n <div style={{ overflowY: \"auto\", borderTop: \"1px solid var(--border-subtle)\" }}>\n <DataTable dense columns={columns} rows={rows} headerBorder={false} />\n </div>\n ) : null}\n </div>\n );\n}\n","pipeline/DataPreview.vue":"<script setup>\nimport IconButton from '../buttons/IconButton.vue'\nimport Checkbox from '../forms/Checkbox.vue'\nimport DataTable from '../data/DataTable.vue'\n// Bottom data-preview drawer — 250px: 40px header (title, Debug Mode, collapse) + dense table.\ndefineProps({ title: { type: String, default: 'Data Preview' }, columns: { type: Array, default: () => [] }, rows: { type: Array, default: () => [] }, debug: Boolean, collapsed: Boolean, style: Object })\nconst emit = defineEmits(['update:debug', 'toggleCollapse'])\n</script>\n<template>\n <div :style=\"{ background: 'var(--bg-surface)', borderTop: '1px solid var(--border-card)', display: 'flex', flexDirection: 'column', height: collapsed ? 40 : 'var(--drawer-h)', overflow: 'hidden', boxSizing: 'border-box', flexShrink: 0, transition: 'height .15s ease', ...style }\">\n <div style=\"height:40px;display:flex;align-items:center;gap:12px;padding:0 16px;box-sizing:border-box;flex-shrink:0\">\n <span style=\"font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-heading)\">{{ title }}</span>\n <span style=\"flex:1\" />\n <Checkbox label=\"Debug Mode\" :checked=\"debug\" @change=\"v => emit('update:debug', v)\" />\n <IconButton :icon=\"collapsed ? 'chevron-up' : 'chevron-down'\" size=\"xs\" muted @click=\"emit('toggleCollapse')\" />\n </div>\n <div v-if=\"!collapsed\" style=\"overflow-y:auto;border-top:1px solid var(--border-subtle)\">\n <DataTable dense :columns=\"columns\" :rows=\"rows\" :header-border=\"false\" />\n </div>\n </div>\n</template>\n","pipeline/NodeCard.jsx":"import React from \"react\";\nimport { Icon } from \"../icons/Icon.jsx\";\nimport { IconTile, StatusBadge } from \"../display/Badge.jsx\";\n\n/* Ports are the connection handles: they light up on hover, stay lit while a wire is\n being dragged out of them, and show a crosshair when they're a valid drop target. */\nfunction portSkin(tone, hover, interactive) {\n const hot = tone === \"active\" || tone === \"target\" || (interactive && hover);\n return {\n background: tone === \"target\" ? \"var(--soft-bg)\" : \"var(--bg-surface)\",\n border: `2px solid ${hot ? \"var(--action-primary)\" : \"var(--port-border)\"}`,\n boxShadow: tone === \"active\" || tone === \"target\" ? \"0 0 0 3px var(--focus-ring)\" : \"none\",\n color: hot ? \"var(--action-primary)\" : \"var(--text-muted)\",\n cursor: interactive ? (tone === \"active\" ? \"grabbing\" : \"grab\") : \"default\",\n transition: \"var(--t-hover)\",\n };\n}\n\n/** Round output/input port — 22px. tone: \"default\" | \"active\" (dragging from it) | \"target\" (drop here). */\nexport function Port({ tone = \"default\", interactive = false, style, onMouseEnter, onMouseLeave, ...rest }) {\n const [hover, setHover] = React.useState(false);\n return (\n <span {...rest}\n onMouseEnter={(e) => { setHover(true); onMouseEnter && onMouseEnter(e); }}\n onMouseLeave={(e) => { setHover(false); onMouseLeave && onMouseLeave(e); }}\n style={{ width: 22, height: 22, borderRadius: \"var(--radius-full)\", boxSizing: \"border-box\", display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\", ...portSkin(tone, hover, interactive), ...style }}>\n <Icon name={tone === \"target\" ? \"plus\" : \"arrow-right\"} size={12} strokeWidth={2.2} />\n </span>\n );\n}\n\n/** Labeled input port pill (\"A >\", \"B >\") — 28×22, same three tones as Port. */\nexport function PortLabel({ label = \"A\", tone = \"default\", interactive = false, style, onMouseEnter, onMouseLeave, ...rest }) {\n const [hover, setHover] = React.useState(false);\n return (\n <span {...rest}\n onMouseEnter={(e) => { setHover(true); onMouseEnter && onMouseEnter(e); }}\n onMouseLeave={(e) => { setHover(false); onMouseLeave && onMouseLeave(e); }}\n style={{ width: 28, height: 22, borderRadius: \"var(--radius-full)\", boxSizing: \"border-box\", display: \"inline-flex\", alignItems: \"center\", justifyContent: \"center\", gap: 2, fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-xs)\", letterSpacing: \"var(--tracking-xs)\", lineHeight: 1, ...portSkin(tone, hover, interactive), ...style }}>\n {tone === \"target\" ? <Icon name=\"plus\" size={12} strokeWidth={2.2} /> : <>{label}<Icon name=\"chevron-right\" size={9} strokeWidth={2.5} /></>}\n </span>\n );\n}\n\n/**\n * Canvas node card — 220×90, r16, soft shadow. Selected = blue border + blue-100 ring.\n * badge: \"error\" | \"warning\" | \"clock\" overlay on the icon tile.\n * Port interaction is driven by the canvas: onOutputDown starts a wire, onInputUp lands it,\n * and outputTone / inputTones light the handles.\n */\nexport function NodeCard({ title, subtitle, icon = \"table\", subtitleIcon, hue = \"source\", selected, badge, inputs = [], output = true, showMenu = true, onClick, onMenu, onOutputDown, onInputUp, onInputEnter, onInputLeave, outputTone = \"default\", inputTones = [], style }) {\n const live = !!(onOutputDown || onInputUp);\n return (\n <div onClick={onClick} style={{ position: \"relative\", width: 220, height: 90, borderRadius: \"var(--radius-node)\", background: \"var(--bg-surface)\", border: `1px solid ${selected ? \"var(--selected-border)\" : \"var(--border-control)\"}`, boxShadow: selected ? \"var(--shadow-selected)\" : \"var(--shadow-node)\", boxSizing: \"border-box\", cursor: \"pointer\", ...style }}>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 12, padding: 16, height: \"100%\", boxSizing: \"border-box\" }}>\n <span style={{ position: \"relative\", flexShrink: 0 }}>\n <IconTile icon={icon} hue={hue} kind=\"node\" />\n {badge === \"error\" ? <StatusBadge tone=\"danger\" icon=\"bug\" size={20} style={{ position: \"absolute\", top: -6, right: -6 }} /> : null}\n {badge === \"warning\" ? <StatusBadge tone=\"warning\" icon=\"triangle-alert\" size={20} style={{ position: \"absolute\", top: -6, right: -6 }} /> : null}\n {badge === \"clock\" ? <StatusBadge tone=\"pending\" icon=\"clock\" size={20} style={{ position: \"absolute\", top: -6, right: -6 }} /> : null}\n </span>\n <div style={{ display: \"flex\", flexDirection: \"column\", minWidth: 0, flex: 1, paddingRight: 18 }}>\n <span style={{ fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-semibold)\", fontSize: \"var(--text-base)\", lineHeight: \"17.875px\", letterSpacing: \"var(--tracking-base)\", color: \"var(--text-heading)\", whiteSpace: \"nowrap\", overflow: \"hidden\", textOverflow: \"ellipsis\" }}>{title}</span>\n <span style={{ display: \"flex\", alignItems: \"flex-start\", gap: 4, paddingTop: 4, minWidth: 0 }}>\n {subtitleIcon !== false ? <span style={{ color: \"var(--text-muted)\", display: \"inline-flex\", flexShrink: 0, paddingTop: 1 }}><Icon name={subtitleIcon || \"info\"} size={13} /></span> : null}\n {/* Node types run long (\"Field Value Calculation\"), so the subtitle wraps to a\n second line and only then clamps — the card size never changes. */}\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-xs)\", lineHeight: \"13.75px\", letterSpacing: \"var(--tracking-xs)\", color: \"var(--text-muted)\", minWidth: 0, overflow: \"hidden\", display: \"-webkit-box\", WebkitBoxOrient: \"vertical\", WebkitLineClamp: 2, overflowWrap: \"break-word\" }}>{subtitle}</span>\n </span>\n </div>\n </div>\n {showMenu ? (\n <button type=\"button\" onClick={(e) => { e.stopPropagation(); onMenu && onMenu(e); }} style={{ position: \"absolute\", top: 10, right: 12, width: 23, height: 23, borderRadius: \"var(--radius-md)\", border: \"none\", background: \"transparent\", cursor: \"pointer\", display: \"flex\", alignItems: \"center\", justifyContent: \"center\", color: \"var(--text-muted)\", padding: 0 }}\n onMouseEnter={(e) => e.currentTarget.style.background = \"var(--bg-hover)\"} onMouseLeave={(e) => e.currentTarget.style.background = \"transparent\"}>\n <Icon name=\"ellipsis\" size={15} />\n </button>\n ) : null}\n {inputs.length === 0 ? null : inputs.length === 1 && !inputs[0] ? (\n <Port tone={inputTones[0] || \"default\"} interactive={live}\n onMouseEnter={onInputEnter ? () => onInputEnter(0) : undefined} onMouseLeave={onInputLeave ? () => onInputLeave(0) : undefined}\n onPointerUp={onInputUp ? (e) => onInputUp(0, e) : undefined}\n style={{ position: \"absolute\", left: -11, top: 33 }} />\n ) : inputs.map((l, i) => (\n <PortLabel key={i} label={l} tone={inputTones[i] || \"default\"} interactive={live}\n onMouseEnter={onInputEnter ? () => onInputEnter(i) : undefined} onMouseLeave={onInputLeave ? () => onInputLeave(i) : undefined}\n onPointerUp={onInputUp ? (e) => onInputUp(i, e) : undefined}\n style={{ position: \"absolute\", left: -14, top: inputs.length === 1 ? 33 : 12 + i * 38 }} />\n ))}\n {output ? (\n <Port tone={outputTone} interactive={live}\n onPointerDown={onOutputDown ? (e) => { e.stopPropagation(); onOutputDown(e); } : undefined}\n onClick={onOutputDown ? (e) => e.stopPropagation() : undefined}\n style={{ position: \"absolute\", right: -11, top: 33 }} />\n ) : null}\n </div>\n );\n}\n","pipeline/NodeCard.vue":"<script setup>\nimport { ref } from 'vue'\nimport Icon from '../icons/Icon.vue'\nimport { IconTile, StatusBadge } from '../display/Badge.vue'\n/**\n * Canvas node card — 220×90, r16, soft shadow. Selected = blue border + blue-100 ring.\n * badge: \"error\" | \"warning\" | \"clock\" overlay on the icon tile.\n * Ports light up on hover, stay lit while a wire is dragged out (outputTone=\"active\"),\n * and show a crosshair when they are a valid drop target (inputTones[i]=\"target\").\n */\nconst props = defineProps({ title: String, subtitle: String, icon: { type: String, default: 'table' }, subtitleIcon: { type: [String, Boolean], default: 'info' }, hue: { type: String, default: 'source' }, selected: Boolean, badge: String, inputs: { type: Array, default: () => [] }, output: { type: Boolean, default: true }, showMenu: { type: Boolean, default: true }, live: Boolean, outputTone: { type: String, default: 'default' }, inputTones: { type: Array, default: () => [] }, style: Object })\nconst emit = defineEmits(['click', 'menu', 'outputDown', 'inputUp', 'inputEnter', 'inputLeave'])\nconst hoverOut = ref(false)\nconst hoverIn = ref(-1)\nfunction skin(tone, hover) {\n const hot = tone === 'active' || tone === 'target' || (props.live && hover)\n return {\n background: tone === 'target' ? 'var(--soft-bg)' : 'var(--bg-surface)',\n border: `2px solid ${hot ? 'var(--action-primary)' : 'var(--port-border)'}`,\n boxShadow: tone === 'active' || tone === 'target' ? '0 0 0 3px var(--focus-ring)' : 'none',\n color: hot ? 'var(--action-primary)' : 'var(--text-muted)',\n cursor: props.live ? (tone === 'active' ? 'grabbing' : 'grab') : 'default',\n transition: 'var(--t-hover)',\n }\n}\nconst portBase = { width: '22px', height: '22px', borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }\nconst pillBase = { width: '28px', height: '22px', borderRadius: 'var(--radius-full)', boxSizing: 'border-box', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: '2px', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-xs)', letterSpacing: 'var(--tracking-xs)', lineHeight: 1 }\nconst tone = i => props.inputTones[i] || 'default'\n</script>\n<template>\n <div @click=\"emit('click')\" :style=\"{ position: 'relative', width: '220px', height: '90px', borderRadius: 'var(--radius-node)', background: 'var(--bg-surface)', border: `1px solid ${selected ? 'var(--selected-border)' : 'var(--border-control)'}`, boxShadow: selected ? 'var(--shadow-selected)' : 'var(--shadow-node)', boxSizing: 'border-box', cursor: 'pointer', ...style }\">\n <div style=\"display:flex;align-items:center;gap:12px;padding:16px;height:100%;box-sizing:border-box\">\n <span style=\"position:relative;flex-shrink:0\">\n <IconTile :icon=\"icon\" :hue=\"hue\" kind=\"node\" />\n <StatusBadge v-if=\"badge === 'error'\" tone=\"danger\" icon=\"bug\" :size=\"20\" style=\"position:absolute;top:-6px;right:-6px\" />\n <StatusBadge v-if=\"badge === 'warning'\" tone=\"warning\" icon=\"triangle-alert\" :size=\"20\" style=\"position:absolute;top:-6px;right:-6px\" />\n <StatusBadge v-if=\"badge === 'clock'\" tone=\"pending\" icon=\"clock\" :size=\"20\" style=\"position:absolute;top:-6px;right:-6px\" />\n </span>\n <div style=\"display:flex;flex-direction:column;min-width:0;flex:1;padding-right:18px\">\n <span style=\"font-family:var(--font-sans);font-weight:var(--weight-semibold);font-size:var(--text-base);line-height:17.875px;letter-spacing:var(--tracking-base);color:var(--text-heading);white-space:nowrap;overflow:hidden;text-overflow:ellipsis\">{{ title }}</span>\n <span style=\"display:flex;align-items:flex-start;gap:4px;padding-top:4px;min-width:0\">\n <span v-if=\"subtitleIcon !== false\" style=\"color:var(--text-muted);display:inline-flex;flex-shrink:0;padding-top:1px\"><Icon :name=\"subtitleIcon || 'info'\" :size=\"13\" /></span>\n <!-- Node types run long (\"Field Value Calculation\"): wrap to a second line, then clamp. -->\n <span style=\"font-family:var(--font-sans);font-size:var(--text-xs);line-height:13.75px;letter-spacing:var(--tracking-xs);color:var(--text-muted);min-width:0;overflow:hidden;display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow-wrap:break-word\">{{ subtitle }}</span>\n </span>\n </div>\n </div>\n <button v-if=\"showMenu\" type=\"button\" @click.stop=\"emit('menu', $event)\" class=\"kb-node-menu\" style=\"position:absolute;top:10px;right:12px;width:23px;height:23px;border-radius:var(--radius-md);border:none;background:transparent;cursor:pointer;display:flex;align-items:center;justify-content:center;color:var(--text-muted);padding:0\">\n <Icon name=\"ellipsis\" :size=\"15\" />\n </button>\n <template v-if=\"inputs.length\">\n <span v-if=\"inputs.length === 1 && !inputs[0]\"\n @mouseenter=\"hoverIn = 0; emit('inputEnter', 0)\" @mouseleave=\"hoverIn = -1; emit('inputLeave', 0)\"\n @pointerup=\"emit('inputUp', 0, $event)\"\n :style=\"{ ...portBase, ...skin(tone(0), hoverIn === 0), position: 'absolute', left: '-11px', top: '33px' }\">\n <Icon :name=\"tone(0) === 'target' ? 'plus' : 'arrow-right'\" :size=\"12\" :stroke-width=\"2.2\" />\n </span>\n <span v-else v-for=\"(l, i) in inputs\" :key=\"i\"\n @mouseenter=\"hoverIn = i; emit('inputEnter', i)\" @mouseleave=\"hoverIn = -1; emit('inputLeave', i)\"\n @pointerup=\"emit('inputUp', i, $event)\"\n :style=\"{ ...pillBase, ...skin(tone(i), hoverIn === i), position: 'absolute', left: '-14px', top: (inputs.length === 1 ? 33 : 12 + i * 38) + 'px' }\">\n <Icon v-if=\"tone(i) === 'target'\" name=\"plus\" :size=\"12\" :stroke-width=\"2.2\" />\n <template v-else>{{ l }}<Icon name=\"chevron-right\" :size=\"9\" :stroke-width=\"2.5\" /></template>\n </span>\n </template>\n <span v-if=\"output\" @mouseenter=\"hoverOut = true\" @mouseleave=\"hoverOut = false\"\n @pointerdown.stop=\"emit('outputDown', $event)\" @click.stop\n :style=\"{ ...portBase, ...skin(outputTone, hoverOut), position: 'absolute', right: '-11px', top: '33px' }\">\n <Icon name=\"arrow-right\" :size=\"12\" :stroke-width=\"2.2\" />\n </span>\n </div>\n</template>\n<style scoped>\n.kb-node-menu:hover{background:var(--bg-hover)}\n</style>\n","pipeline/PaletteItem.vue":"<script setup>\nimport { ref } from 'vue'\nimport { IconTile } from '../display/Badge.vue'\n// Palette grid tile — 40px tinted icon + 10px two-line label.\ndefineProps({ icon: String, hue: String, label: String, style: Object })\ndefineEmits(['click'])\nconst hover = ref(false)\n</script>\n<template>\n <div role=\"button\" tabindex=\"0\" @click=\"$emit('click')\" @mouseenter=\"hover = true\" @mouseleave=\"hover = false\"\n :style=\"{ width: 67.664, borderRadius: 'var(--radius-lg)', padding: 8, display: 'flex', flexDirection: 'column', gap: 6, alignItems: 'center', cursor: 'grab', boxSizing: 'border-box', background: hover ? 'var(--bg-hover)' : 'transparent', transition: 'background .12s ease', ...style }\">\n <IconTile :icon=\"icon\" :hue=\"hue\" kind=\"palette\" />\n <span style=\"font-family:var(--font-sans);font-size:var(--text-2xs);line-height:var(--leading-2xs);letter-spacing:var(--tracking-2xs);color:var(--text-body);text-align:center\">{{ label }}</span>\n </div>\n</template>\n","pipeline/PaletteListItem.vue":"<script setup>\nimport { ref } from 'vue'\nimport Icon from '../icons/Icon.vue'\n// Palette list row — 14px hued glyph + 13px label, indented under its category.\ndefineProps({ icon: String, hue: String, label: String, style: Object })\ndefineEmits(['click'])\nconst hover = ref(false)\n</script>\n<template>\n <div role=\"button\" tabindex=\"0\" @click=\"$emit('click')\" @mouseenter=\"hover = true\" @mouseleave=\"hover = false\"\n :style=\"{ height: 'var(--h-subnav-item)', borderRadius: 'var(--radius-md)', display: 'flex', alignItems: 'center', gap: 8, padding: '6px 8px', boxSizing: 'border-box', cursor: 'grab', background: hover ? 'var(--bg-hover)' : 'transparent', ...style }\">\n <span :style=\"{ color: `var(--cat-${hue})`, display: 'inline-flex' }\"><Icon :name=\"icon\" :size=\"14\" /></span>\n <span style=\"font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-body);white-space:nowrap\">{{ label }}</span>\n </div>\n</template>\n","pipeline/PalettePanel.jsx":"import React from \"react\";\nimport { Icon } from \"../icons/Icon.jsx\";\nimport { IconTile } from \"../display/Badge.jsx\";\nimport { ViewToggle } from \"../tabs/ViewToggle.jsx\";\n\n/** Palette grid tile — 40px tinted icon + 10px two-line label. */\nexport function PaletteItem({ icon, hue, label, onClick, style }) {\n const [hover, setHover] = React.useState(false);\n return (\n <div role=\"button\" tabIndex={0} onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}\n style={{ width: 67.664, borderRadius: \"var(--radius-lg)\", padding: 8, display: \"flex\", flexDirection: \"column\", gap: 6, alignItems: \"center\", cursor: \"grab\", boxSizing: \"border-box\", background: hover ? \"var(--bg-hover)\" : \"transparent\", transition: \"background .12s ease\", ...style }}>\n <IconTile icon={icon} hue={hue} kind=\"palette\" />\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-2xs)\", lineHeight: \"var(--leading-2xs)\", letterSpacing: \"var(--tracking-2xs)\", color: \"var(--text-body)\", textAlign: \"center\" }}>{label}</span>\n </div>\n );\n}\n\n/** Palette list row — 14px hued glyph + 13px label, indented under its category. */\nexport function PaletteListItem({ icon, hue, label, onClick, style }) {\n const [hover, setHover] = React.useState(false);\n return (\n <div role=\"button\" tabIndex={0} onClick={onClick} onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}\n style={{ height: \"var(--h-subnav-item)\", borderRadius: \"var(--radius-md)\", display: \"flex\", alignItems: \"center\", gap: 8, padding: \"6px 8px\", boxSizing: \"border-box\", cursor: \"grab\", background: hover ? \"var(--bg-hover)\" : \"transparent\", ...style }}>\n <span style={{ color: `var(--cat-${hue})`, display: \"inline-flex\" }}><Icon name={icon} size={14} /></span>\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-base)\", lineHeight: \"var(--leading-base)\", letterSpacing: \"var(--tracking-base)\", color: \"var(--text-body)\", whiteSpace: \"nowrap\" }}>{label}</span>\n </div>\n );\n}\n\n/** The full component set of the pipeline editor, verbatim from the file. */\nexport const PIPELINE_CATALOG = [\n { label: \"Data Source\", icon: \"table\", hue: \"source\", items: [{ label: \"Detail Sheet\", icon: \"table\" }, { label: \"Model\", icon: \"layers\" }, { label: \"Dimension\", icon: \"grid-3x3\" }] },\n { label: \"Data Join\", icon: \"text-search\", hue: \"join\", items: [{ label: \"Lookup Data\", icon: \"text-search\" }, { label: \"Association Match\", icon: \"shuffle\" }] },\n { label: \"Data Transformation\", icon: \"sliders-horizontal\", hue: \"transform\", items: [{ label: \"Field Value Calculation\", icon: \"sliders-horizontal\" }, { label: \"Field Split\", icon: \"split\" }, { label: \"Row to Column\", icon: \"arrow-right-left\" }, { label: \"Column to Row\", icon: \"menu\" }, { label: \"Multi-Condition Judge\", icon: \"funnel\" }, { label: \"Regional Calculation\", icon: \"chart-no-axes-column\" }, { label: \"Advanced Transformation\", icon: \"trending-up\" }, { label: \"JS Enhanced Processing\", icon: \"code\" }] },\n { label: \"Data Merge\", icon: \"database\", hue: \"merge\", items: [{ label: \"Group Calculation\", icon: \"database\" }, { label: \"Concat Merge\", icon: \"merge\" }] },\n { label: \"Data Output\", icon: \"table\", hue: \"output\", items: [{ label: \"Detail Sheet\", icon: \"table\" }, { label: \"Model\", icon: \"layers\" }, { label: \"Dimension\", icon: \"grid-3x3\" }] },\n];\n\n/** 240px \"Components\" palette panel with list/grid toggle and collapsible hued categories. */\nexport function PalettePanel({ catalog = PIPELINE_CATALOG, height = \"100%\", onPick, style }) {\n const [view, setView] = React.useState(\"grid\");\n const [closed, setClosed] = React.useState([]);\n const toggle = (l) => setClosed(closed.includes(l) ? closed.filter((x) => x !== l) : [...closed, l]);\n return (\n <div style={{ width: \"var(--sidebar-w)\", height, background: \"var(--bg-surface)\", borderRight: \"1px solid var(--border-card)\", display: \"flex\", flexDirection: \"column\", boxSizing: \"border-box\", flexShrink: 0, ...style }}>\n <div style={{ height: 43, borderBottom: \"1px solid var(--border-subtle)\", display: \"flex\", alignItems: \"center\", justifyContent: \"space-between\", padding: \"8px 12px\", boxSizing: \"border-box\", flexShrink: 0 }}>\n <span style={{ fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-base)\", lineHeight: \"var(--leading-base)\", letterSpacing: \"var(--tracking-base)\", color: \"var(--text-body)\" }}>Components</span>\n <ViewToggle value={view} onChange={setView} />\n </div>\n <div style={{ flex: 1, overflowY: \"auto\", padding: 8 }}>\n {catalog.map((cat) => (\n <div key={cat.label}>\n <div onClick={() => toggle(cat.label)} style={{ height: \"var(--h-subnav-item)\", borderRadius: \"var(--radius-md)\", display: \"flex\", alignItems: \"center\", gap: 8, padding: \"6px 8px\", boxSizing: \"border-box\", cursor: \"pointer\" }}>\n <span style={{ color: \"var(--text-muted)\", display: \"inline-flex\", transform: closed.includes(cat.label) ? \"rotate(-90deg)\" : \"none\", transition: \"transform .15s ease\" }}><Icon name=\"chevron-down\" size={14} /></span>\n <span style={{ color: `var(--cat-${cat.hue})`, display: \"inline-flex\" }}><Icon name={cat.icon} size={14} /></span>\n <span style={{ fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-medium)\", fontSize: \"var(--text-base)\", lineHeight: \"var(--leading-base)\", letterSpacing: \"var(--tracking-base)\", color: \"var(--text-strong)\" }}>{cat.label}</span>\n </div>\n {!closed.includes(cat.label) ? (\n view === \"grid\" ? (\n <div style={{ display: \"flex\", flexWrap: \"wrap\", padding: \"8px 0 8px 4px\" }}>\n {cat.items.map((it) => <PaletteItem key={it.label} icon={it.icon} hue={cat.hue} label={it.label} onClick={() => onPick && onPick(cat.label, it.label)} />)}\n </div>\n ) : (\n <div style={{ padding: \"2px 0 6px 20px\", display: \"flex\", flexDirection: \"column\" }}>\n {cat.items.map((it) => <PaletteListItem key={it.label} icon={it.icon} hue={cat.hue} label={it.label} onClick={() => onPick && onPick(cat.label, it.label)} />)}\n </div>\n )\n ) : null}\n </div>\n ))}\n </div>\n </div>\n );\n}\n","pipeline/PalettePanel.vue":"<script setup>\nimport { ref } from 'vue'\nimport Icon from '../icons/Icon.vue'\nimport { IconTile } from '../display/Badge.vue'\nimport ViewToggle from '../tabs/ViewToggle.vue'\nimport PaletteItem from './PaletteItem.vue'\nimport PaletteListItem from './PaletteListItem.vue'\n\nexport const PIPELINE_CATALOG = [\n { label: 'Data Source', icon: 'table', hue: 'source', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },\n { label: 'Data Join', icon: 'text-search', hue: 'join', items: [{ label: 'Lookup Data', icon: 'text-search' }, { label: 'Association Match', icon: 'shuffle' }] },\n { label: 'Data Transformation', icon: 'sliders-horizontal', hue: 'transform', items: [{ label: 'Field Value Calculation', icon: 'sliders-horizontal' }, { label: 'Field Split', icon: 'split' }, { label: 'Row to Column', icon: 'arrow-right-left' }, { label: 'Column to Row', icon: 'menu' }, { label: 'Multi-Condition Judge', icon: 'funnel' }, { label: 'Regional Calculation', icon: 'chart-no-axes-column' }, { label: 'Advanced Transformation', icon: 'trending-up' }, { label: 'JS Enhanced Processing', icon: 'code' }] },\n { label: 'Data Merge', icon: 'database', hue: 'merge', items: [{ label: 'Group Calculation', icon: 'database' }, { label: 'Concat Merge', icon: 'merge' }] },\n { label: 'Data Output', icon: 'table', hue: 'output', items: [{ label: 'Detail Sheet', icon: 'table' }, { label: 'Model', icon: 'layers' }, { label: 'Dimension', icon: 'grid-3x3' }] },\n]\n// 240px \"Components\" palette panel with list/grid toggle and collapsible hued categories.\nconst props = defineProps({ catalog: { type: Array, default: () => PIPELINE_CATALOG }, height: { type: [String, Number], default: '100%' }, style: Object })\nconst emit = defineEmits(['pick'])\nconst view = ref('grid')\nconst closed = ref([])\nfunction toggle(l) { closed.value = closed.value.includes(l) ? closed.value.filter(x => x !== l) : [...closed.value, l] }\n</script>\n<template>\n <div :style=\"{ width: 'var(--sidebar-w)', height, background: 'var(--bg-surface)', borderRight: '1px solid var(--border-card)', display: 'flex', flexDirection: 'column', boxSizing: 'border-box', flexShrink: 0, ...style }\">\n <div style=\"height:43px;border-bottom:1px solid var(--border-subtle);display:flex;align-items:center;justify-content:space-between;padding:8px 12px;box-sizing:border-box;flex-shrink:0\">\n <span style=\"font-family:var(--font-sans);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-body)\">Components</span>\n <ViewToggle v-model=\"view\" />\n </div>\n <div style=\"flex:1;overflow-y:auto;padding:8px\">\n <div v-for=\"cat in catalog\" :key=\"cat.label\">\n <div @click=\"toggle(cat.label)\" style=\"height:var(--h-subnav-item);border-radius:var(--radius-md);display:flex;align-items:center;gap:8px;padding:6px 8px;box-sizing:border-box;cursor:pointer\">\n <span :style=\"{ color: 'var(--text-muted)', display: 'inline-flex', transform: closed.includes(cat.label) ? 'rotate(-90deg)' : 'none', transition: 'transform .15s ease' }\"><Icon name=\"chevron-down\" :size=\"14\" /></span>\n <span :style=\"{ color: `var(--cat-${cat.hue})`, display: 'inline-flex' }\"><Icon :name=\"cat.icon\" :size=\"14\" /></span>\n <span style=\"font-family:var(--font-sans);font-weight:var(--weight-medium);font-size:var(--text-base);line-height:var(--leading-base);letter-spacing:var(--tracking-base);color:var(--text-strong)\">{{ cat.label }}</span>\n </div>\n <div v-if=\"!closed.includes(cat.label) && view === 'grid'\" style=\"display:flex;flex-wrap:wrap;padding:8px 0 8px 4px\">\n <PaletteItem v-for=\"it in cat.items\" :key=\"it.label\" :icon=\"it.icon\" :hue=\"cat.hue\" :label=\"it.label\" @click=\"emit('pick', cat.label, it.label)\" />\n </div>\n <div v-else-if=\"!closed.includes(cat.label)\" style=\"padding:2px 0 6px 20px;display:flex;flex-direction:column\">\n <PaletteListItem v-for=\"it in cat.items\" :key=\"it.label\" :icon=\"it.icon\" :hue=\"cat.hue\" :label=\"it.label\" @click=\"emit('pick', cat.label, it.label)\" />\n </div>\n </div>\n </div>\n </div>\n</template>\n","pipeline/PipelineCanvas.jsx":"import React from \"react\";\nimport { NodeCard } from \"./NodeCard.jsx\";\n\nconst NODE_W = 220;\nconst outPt = (n) => ({ x: n.x + NODE_W, y: n.y + 44 });\nconst inPt = (n, portIdx = 0) => {\n const multi = (n.inputs || []).filter(Boolean).length > 1;\n return { x: n.x, y: multi ? n.y + 23 + portIdx * 38 : n.y + 44 };\n};\n/** Horizontal cubic bezier; the control offset keeps a backward link bowed out of the cards. */\nconst curve = (p1, p2) => {\n const dx = Math.max(40, (p2.x - p1.x) / 2);\n return `M ${p1.x} ${p1.y} C ${p1.x + dx} ${p1.y}, ${p2.x - dx} ${p2.y}, ${p2.x} ${p2.y}`;\n};\n\n/**\n * Pipeline canvas: #fafafa surface, slate bezier edges, absolutely-placed NodeCards.\n * nodes: [{id,x,y,...NodeCardProps}]; edges: [{from,to,toPort}] (toPort index for A/B inputs).\n * With onConnect, holding an output port drags a dashed wire; input ports light up as drop\n * targets and the drop reports {from,to,toPort}. scale tells the canvas its zoom factor so\n * pointer coordinates stay in canvas space.\n */\nexport function PipelineCanvas({ nodes = [], edges = [], selectedId, onSelect, onMenu, onConnect, snapRadius = 30, scale = 1, width = \"100%\", height = \"100%\", style }) {\n const byId = Object.fromEntries(nodes.map((n) => [n.id, n]));\n const ref = React.useRef(null);\n const [drag, setDrag] = React.useState(null); // {from, x, y, over:{id,port}}\n\n /* The drop target is hit-tested from the pointer position rather than port hover:\n during a drag the cursor is often over the wire layer, not the 22px handle. */\n const hitTest = React.useCallback((x, y, fromId) => {\n let best = null;\n nodes.forEach((n) => {\n if (n.id === fromId) return;\n const ports = n.inputs || [];\n if (!ports.length) return;\n ports.forEach((_, i) => {\n const p = inPt(n, i);\n const d = Math.hypot(p.x - x, p.y - y);\n if (d <= snapRadius && (!best || d < best.d)) best = { id: n.id, port: i, d };\n });\n });\n return best ? { id: best.id, port: best.port } : null;\n }, [nodes, snapRadius]);\n\n React.useEffect(() => {\n if (!drag) return;\n const move = (e) => {\n const r = ref.current.getBoundingClientRect();\n const x = (e.clientX - r.left) / scale, y = (e.clientY - r.top) / scale;\n setDrag((d) => d && { ...d, x, y, over: hitTest(x, y, d.from) });\n };\n const up = () => setDrag((d) => {\n if (d && d.over && onConnect) onConnect({ from: d.from, to: d.over.id, toPort: d.over.port });\n return null;\n });\n window.addEventListener(\"pointermove\", move);\n window.addEventListener(\"pointerup\", up);\n return () => { window.removeEventListener(\"pointermove\", move); window.removeEventListener(\"pointerup\", up); };\n }, [drag, scale, onConnect, hitTest]);\n\n const start = (id, e) => {\n const r = ref.current.getBoundingClientRect();\n setDrag({ from: id, x: (e.clientX - r.left) / scale, y: (e.clientY - r.top) / scale, over: null });\n };\n\n const path = (e) => {\n const a = byId[e.from], b = byId[e.to];\n return a && b ? curve(outPt(a), inPt(b, e.toPort || 0)) : \"\";\n };\n const dragPath = () => {\n const a = byId[drag.from];\n if (!a) return \"\";\n const end = drag.over && byId[drag.over.id] ? inPt(byId[drag.over.id], drag.over.port) : { x: drag.x, y: drag.y };\n return curve(outPt(a), end);\n };\n\n return (\n <div ref={ref} style={{ position: \"relative\", width, height, background: \"var(--bg-app)\", overflow: \"hidden\", cursor: drag ? \"grabbing\" : \"default\", ...style }}>\n <svg style={{ position: \"absolute\", inset: 0, width: \"100%\", height: \"100%\", pointerEvents: \"none\", overflow: \"visible\" }}>\n {edges.map((e, i) => <path key={i} d={path(e)} fill=\"none\" stroke=\"var(--edge)\" strokeWidth=\"1.5\" />)}\n {drag ? <path d={dragPath()} fill=\"none\" stroke=\"var(--action-primary)\" strokeWidth=\"1.5\" strokeDasharray=\"5 4\" /> : null}\n </svg>\n {nodes.map(({ id, x, y, ...props }) => (\n <NodeCard key={id} {...props} selected={selectedId === id}\n onClick={() => !drag && onSelect && onSelect(id)} onMenu={(e) => onMenu && onMenu(id, e)}\n onOutputDown={onConnect ? (e) => start(id, e) : undefined}\n onInputUp={onConnect ? () => {} : undefined}\n outputTone={drag && drag.from === id ? \"active\" : \"default\"}\n inputTones={drag && drag.over && drag.over.id === id ? Object.assign([], { [drag.over.port]: \"target\" }) : []}\n style={{ position: \"absolute\", left: x, top: y }} />\n ))}\n </div>\n );\n}\n","pipeline/PipelineCanvas.vue":"<script setup>\nimport { computed, ref, onUnmounted } from 'vue'\nimport NodeCard from './NodeCard.vue'\n/**\n * Pipeline canvas: #fafafa surface, slate bezier edges, absolutely-placed NodeCards.\n * nodes: [{id,x,y,...NodeCardProps}]; edges: [{from,to,toPort}] (toPort index for A/B inputs).\n * With connectable, holding an output port drags a dashed wire; input ports light up as drop\n * targets and the drop emits connect {from,to,toPort}. scale tells the canvas its zoom factor.\n */\nconst props = defineProps({ nodes: { type: Array, default: () => [] }, edges: { type: Array, default: () => [] }, selectedId: [String, Number], connectable: Boolean, snapRadius: { type: Number, default: 30 }, scale: { type: Number, default: 1 }, width: { type: [String, Number], default: '100%' }, height: { type: [String, Number], default: '100%' }, style: Object })\nconst emit = defineEmits(['select', 'menu', 'connect'])\nconst NODE_W = 220\nconst root = ref(null)\nconst drag = ref(null)\nconst byId = computed(() => Object.fromEntries(props.nodes.map(n => [n.id, n])))\nfunction outPt(n) { return { x: n.x + NODE_W, y: n.y + 44 } }\nfunction inPt(n, portIdx = 0) { const multi = (n.inputs || []).filter(Boolean).length > 1; return { x: n.x, y: multi ? n.y + 23 + portIdx * 38 : n.y + 44 } }\nfunction curve(p1, p2) { const dx = Math.max(40, (p2.x - p1.x) / 2); return `M ${p1.x} ${p1.y} C ${p1.x + dx} ${p1.y}, ${p2.x - dx} ${p2.y}, ${p2.x} ${p2.y}` }\nfunction path(e) { const a = byId.value[e.from], b = byId.value[e.to]; return a && b ? curve(outPt(a), inPt(b, e.toPort || 0)) : '' }\nconst dragPath = computed(() => {\n const d = drag.value; if (!d) return ''\n const a = byId.value[d.from]; if (!a) return ''\n const end = d.over && byId.value[d.over.id] ? inPt(byId.value[d.over.id], d.over.port) : { x: d.x, y: d.y }\n return curve(outPt(a), end)\n})\nfunction at(e) { const r = root.value.getBoundingClientRect(); return { x: (e.clientX - r.left) / props.scale, y: (e.clientY - r.top) / props.scale } }\n/* The drop target is hit-tested from the pointer position rather than port hover:\n during a drag the cursor is often over the wire layer, not the 22px handle. */\nfunction hitTest(x, y, fromId) {\n let best = null\n for (const n of props.nodes) {\n if (n.id === fromId) continue\n const ports = n.inputs || []\n for (let i = 0; i < ports.length; i++) {\n const p = inPt(n, i)\n const d = Math.hypot(p.x - x, p.y - y)\n if (d <= props.snapRadius && (!best || d < best.d)) best = { id: n.id, port: i, d }\n }\n }\n return best ? { id: best.id, port: best.port } : null\n}\nfunction move(e) {\n if (!drag.value) return\n const p = at(e)\n drag.value = { ...drag.value, ...p, over: hitTest(p.x, p.y, drag.value.from) }\n}\nfunction up() {\n const d = drag.value\n if (d && d.over) emit('connect', { from: d.from, to: d.over.id, toPort: d.over.port })\n drag.value = null\n window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up)\n}\nfunction start(id, e) {\n if (!props.connectable) return\n drag.value = { from: id, ...at(e), over: null }\n window.addEventListener('pointermove', move); window.addEventListener('pointerup', up)\n}\nconst inputTones = id => (drag.value && drag.value.over && drag.value.over.id === id ? Object.assign([], { [drag.value.over.port]: 'target' }) : [])\nonUnmounted(() => { window.removeEventListener('pointermove', move); window.removeEventListener('pointerup', up) })\n</script>\n<template>\n <div ref=\"root\" :style=\"{ position: 'relative', width, height, background: 'var(--bg-app)', overflow: 'hidden', cursor: drag ? 'grabbing' : 'default', ...style }\">\n <svg style=\"position:absolute;inset:0;width:100%;height:100%;pointer-events:none;overflow:visible\">\n <path v-for=\"(e, i) in edges\" :key=\"i\" :d=\"path(e)\" fill=\"none\" stroke=\"var(--edge)\" stroke-width=\"1.5\" />\n <path v-if=\"drag\" :d=\"dragPath\" fill=\"none\" stroke=\"var(--action-primary)\" stroke-width=\"1.5\" stroke-dasharray=\"5 4\" />\n </svg>\n <NodeCard v-for=\"n in nodes\" :key=\"n.id\" v-bind=\"n\" :selected=\"selectedId === n.id\" :live=\"connectable\"\n :output-tone=\"drag && drag.from === n.id ? 'active' : 'default'\" :input-tones=\"inputTones(n.id)\"\n @click=\"!drag && emit('select', n.id)\" @menu=\"emit('menu', n.id)\"\n @output-down=\"start(n.id, $event)\"\n :style=\"{ position: 'absolute', left: n.x + 'px', top: n.y + 'px' }\" />\n </div>\n</template>\n","pipeline/ZoomControls.jsx":"import React from \"react\";\nimport { IconButton } from \"../buttons/IconButton.jsx\";\n\n/** Floating canvas zoom control (− / percentage / + / fit-to-view). */\nexport function ZoomControls({ value = 100, onChange, onFit, style }) {\n return (\n <div style={{ display: \"inline-flex\", alignItems: \"center\", gap: 2, background: \"var(--bg-surface)\", border: \"1px solid var(--border-control)\", borderRadius: \"var(--radius-md)\", boxShadow: \"var(--shadow-menu)\", padding: 3, boxSizing: \"border-box\", ...style }}>\n <IconButton icon=\"minus\" size=\"sm\" onClick={() => onChange && onChange(Math.max(25, value - 10))} label=\"Zoom out\" />\n <span style={{ minWidth: 40, textAlign: \"center\", fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-xs)\", color: \"var(--text-secondary)\" }}>{value}%</span>\n <IconButton icon=\"plus\" size=\"sm\" onClick={() => onChange && onChange(Math.min(200, value + 10))} label=\"Zoom in\" />\n <span style={{ width: 1, alignSelf: \"stretch\", background: \"var(--border-subtle)\", margin: \"0 2px\" }} />\n <IconButton icon=\"target\" size=\"sm\" onClick={onFit} label=\"Fit to view\" />\n </div>\n );\n}\n","pipeline/ZoomControls.vue":"<script setup>\nimport IconButton from '../buttons/IconButton.vue'\n// Floating canvas zoom control (− / percentage / + / fit-to-view).\nconst props = defineProps({ modelValue: { type: Number, default: 100 }, style: Object })\nconst emit = defineEmits(['update:modelValue', 'fit'])\n</script>\n<template>\n <div :style=\"{ display: 'inline-flex', alignItems: 'center', gap: 2, background: 'var(--bg-surface)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxShadow: 'var(--shadow-menu)', padding: 3, boxSizing: 'border-box', ...style }\">\n <IconButton icon=\"minus\" size=\"sm\" label=\"Zoom out\" @click=\"emit('update:modelValue', Math.max(25, modelValue - 10))\" />\n <span style=\"min-width:40px;text-align:center;font-family:var(--font-sans);font-size:var(--text-xs);color:var(--text-secondary)\">{{ modelValue }}%</span>\n <IconButton icon=\"plus\" size=\"sm\" label=\"Zoom in\" @click=\"emit('update:modelValue', Math.min(200, modelValue + 10))\" />\n <span style=\"width:1px;align-self:stretch;background:var(--border-subtle);margin:0 2px\" />\n <IconButton icon=\"target\" size=\"sm\" label=\"Fit to view\" @click=\"emit('fit')\" />\n </div>\n</template>\n"});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
window.__KB_SRC__=Object.assign(window.__KB_SRC__||{},{"tabs/SegmentedControl.jsx":"import React from \"react\";\nimport { Icon } from \"../icons/Icon.jsx\";\n\n/** Segmented control — inset track, raised active segment. options: string[] or [{label,icon}]. */\nexport function SegmentedControl({ options = [], value, onChange, size = \"md\", style }) {\n const h = size === \"sm\" ? 26 : 32;\n return (\n <div style={{ display: \"inline-flex\", padding: 2, gap: 2, background: \"var(--bg-inset)\", border: \"1px solid var(--border-control)\", borderRadius: \"var(--radius-md)\", boxSizing: \"border-box\", ...style }}>\n {options.map((o) => {\n const label = typeof o === \"string\" ? o : o.label;\n const is = value === label;\n return (\n <button key={label} type=\"button\" onClick={() => onChange && onChange(label)}\n style={{ height: h - 6, padding: \"0 10px\", display: \"inline-flex\", alignItems: \"center\", gap: 6, borderRadius: \"var(--radius-sm)\", border: \"none\", cursor: \"pointer\", background: is ? \"var(--bg-surface)\" : \"transparent\", boxShadow: is ? \"0 1px 2px rgba(0,0,0,.08)\" : \"none\", fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-medium)\", fontSize: size === \"sm\" ? \"var(--text-xs)\" : \"var(--text-sm)\", color: is ? \"var(--text-heading)\" : \"var(--text-secondary)\", whiteSpace: \"nowrap\", transition: \"var(--t-hover)\" }}>\n {typeof o === \"object\" && o.icon ? <Icon name={o.icon} size={13} /> : null}{label}\n </button>\n );\n })}\n </div>\n );\n}\n","tabs/SegmentedControl.vue":"<script setup>\nimport Icon from '../icons/Icon.vue'\n// Segmented control — inset track, raised active segment. options: string[] or [{label,icon}].\nconst props = defineProps({ options: { type: Array, default: () => [] }, modelValue: String, size: { type: String, default: 'md' }, style: Object })\nconst emit = defineEmits(['update:modelValue'])\nfunction label(o) { return typeof o === 'string' ? o : o.label }\n</script>\n<template>\n <div :style=\"{ display: 'inline-flex', padding: 2, gap: 2, background: 'var(--bg-inset)', border: '1px solid var(--border-control)', borderRadius: 'var(--radius-md)', boxSizing: 'border-box', ...style }\">\n <button v-for=\"o in options\" :key=\"label(o)\" type=\"button\" @click=\"emit('update:modelValue', label(o))\"\n :style=\"{ height: (size === 'sm' ? 26 : 32) - 6, padding: '0 10px', display: 'inline-flex', alignItems: 'center', gap: 6, borderRadius: 'var(--radius-sm)', border: 'none', cursor: 'pointer', background: modelValue === label(o) ? 'var(--bg-surface)' : 'transparent', boxShadow: modelValue === label(o) ? '0 1px 2px rgba(0,0,0,.08)' : 'none', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: size === 'sm' ? 'var(--text-xs)' : 'var(--text-sm)', color: modelValue === label(o) ? 'var(--text-heading)' : 'var(--text-secondary)', whiteSpace: 'nowrap', transition: 'var(--t-hover)' }\">\n <Icon v-if=\"typeof o === 'object' && o.icon\" :name=\"o.icon\" :size=\"13\" />{{ label(o) }}\n </button>\n </div>\n</template>\n","tabs/Tabs.jsx":"import React from \"react\";\n\n/** Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline. */\nexport function Tabs({ tabs = [], active, onChange, style }) {\n return (\n <div style={{ display: \"flex\", gap: 4, borderBottom: \"1px solid var(--border-control)\", ...style }}>\n {tabs.map((t) => {\n const is = t === active;\n return (\n <button key={t} type=\"button\" onClick={() => onChange && onChange(t)}\n style={{ background: \"transparent\", border: \"none\", cursor: \"pointer\", padding: \"9px 16px\", marginBottom: -1, borderBottom: is ? \"2px solid var(--action-primary)\" : \"2px solid transparent\", fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-medium)\", fontSize: \"var(--text-base)\", lineHeight: \"var(--leading-base)\", letterSpacing: \"var(--tracking-base)\", color: is ? \"var(--action-primary)\" : \"var(--text-body)\" }}>\n {t}\n </button>\n );\n })}\n </div>\n );\n}\n\n/** Compact panel tabs (Node Info · Calc Rules · Output Fields · SQL Preview), 12px. */\nexport function PanelTabs({ tabs = [], active, onChange, style }) {\n return (\n <div style={{ display: \"flex\", borderBottom: \"1px solid var(--border-subtle)\", ...style }}>\n {tabs.map((t) => {\n const is = t === active;\n return (\n <button key={t} type=\"button\" onClick={() => onChange && onChange(t)}\n style={{ background: \"transparent\", border: \"none\", cursor: \"pointer\", padding: \"8px 12px\", marginBottom: -1, borderBottom: is ? \"2px solid var(--action-primary)\" : \"2px solid transparent\", fontFamily: \"var(--font-sans)\", fontSize: \"var(--text-sm)\", lineHeight: \"var(--leading-sm)\", color: is ? \"var(--action-primary)\" : \"var(--text-secondary)\" }}>\n {t}\n </button>\n );\n })}\n </div>\n );\n}\n\n/** Text pill filter tabs (For Review · To be Submitted · For Approval). Active = #f5f5f5 pill. */\nexport function PillTabs({ tabs = [], active, onChange, style }) {\n return (\n <div style={{ display: \"flex\", gap: 8, ...style }}>\n {tabs.map((t) => {\n const is = t === active;\n return (\n <button key={t} type=\"button\" onClick={() => onChange && onChange(t)}\n style={{ background: is ? \"var(--bg-active)\" : \"transparent\", border: \"none\", cursor: \"pointer\", height: 28, padding: \"0 11px\", borderRadius: \"var(--radius-md)\", fontFamily: \"var(--font-sans)\", fontWeight: \"var(--weight-medium)\", fontSize: \"var(--text-sm)\", lineHeight: \"var(--leading-sm)\", color: is ? \"var(--text-heading)\" : \"var(--text-secondary)\" }}>\n {t}\n </button>\n );\n })}\n </div>\n );\n}\n","tabs/Tabs.vue":"<script setup>\n// Underline tabs on a hairline baseline (Import/Export). Active = blue text + 2px blue underline.\ndefineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })\nconst emit = defineEmits(['change'])\n</script>\n<template>\n <div :style=\"{ display: 'flex', gap: 4, borderBottom: '1px solid var(--border-control)', ...style }\">\n <button v-for=\"t in tabs\" :key=\"t\" type=\"button\" @click=\"emit('change', t)\"\n :style=\"{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '9px 16px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-base)', lineHeight: 'var(--leading-base)', letterSpacing: 'var(--tracking-base)', color: t === active ? 'var(--action-primary)' : 'var(--text-body)' }\">{{ t }}</button>\n </div>\n</template>\n\n<!-- PanelTabs — compact panel tabs (Node Info · Calc Rules · Output Fields · SQL Preview), 12px. -->\n<!--\n<script setup name=\"PanelTabs\">\ndefineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })\nconst emit = defineEmits(['change'])\n</script>\n<template>\n <div :style=\"{ display: 'flex', borderBottom: '1px solid var(--border-subtle)', ...style }\">\n <button v-for=\"t in tabs\" :key=\"t\" type=\"button\" @click=\"emit('change', t)\"\n :style=\"{ background: 'transparent', border: 'none', cursor: 'pointer', padding: '8px 12px', marginBottom: -1, borderBottom: t === active ? '2px solid var(--action-primary)' : '2px solid transparent', fontFamily: 'var(--font-sans)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--action-primary)' : 'var(--text-secondary)' }\">{{ t }}</button>\n </div>\n</template>\n-->\n\n<!-- PillTabs — text pill filter tabs (For Review · To be Submitted · For Approval). Active = filled pill. -->\n<!--\n<script setup name=\"PillTabs\">\ndefineProps({ tabs: { type: Array, default: () => [] }, active: String, style: Object })\nconst emit = defineEmits(['change'])\n</script>\n<template>\n <div :style=\"{ display: 'flex', gap: 8, ...style }\">\n <button v-for=\"t in tabs\" :key=\"t\" type=\"button\" @click=\"emit('change', t)\"\n :style=\"{ background: t === active ? 'var(--bg-active)' : 'transparent', border: 'none', cursor: 'pointer', height: 28, padding: '0 11px', borderRadius: 'var(--radius-md)', fontFamily: 'var(--font-sans)', fontWeight: 'var(--weight-medium)', fontSize: 'var(--text-sm)', lineHeight: 'var(--leading-sm)', color: t === active ? 'var(--text-heading)' : 'var(--text-secondary)' }\">{{ t }}</button>\n </div>\n</template>\n-->\n","tabs/ViewToggle.jsx":"import React from \"react\";\nimport { IconButton } from \"../buttons/IconButton.jsx\";\n\n/** Two-state list/grid icon toggle (palette header). */\nexport function ViewToggle({ value = \"grid\", onChange, style }) {\n return (\n <div style={{ display: \"inline-flex\", gap: 2, ...style }}>\n <IconButton icon=\"list\" size=\"sm\" active={value === \"list\"} onClick={() => onChange && onChange(\"list\")} label=\"List view\" />\n <IconButton icon=\"layout-grid\" size=\"sm\" active={value === \"grid\"} onClick={() => onChange && onChange(\"grid\")} label=\"Grid view\" />\n </div>\n );\n}\n","tabs/ViewToggle.vue":"<script setup>\nimport IconButton from '../buttons/IconButton.vue'\n// Two-state list/grid icon toggle (palette header).\ndefineProps({ modelValue: { type: String, default: 'grid' }, style: Object })\nconst emit = defineEmits(['update:modelValue'])\n</script>\n<template>\n <div :style=\"{ display: 'inline-flex', gap: 2, ...style }\">\n <IconButton icon=\"list\" size=\"sm\" :active=\"modelValue === 'list'\" label=\"List view\" @click=\"emit('update:modelValue', 'list')\" />\n <IconButton icon=\"layout-grid\" size=\"sm\" :active=\"modelValue === 'grid'\" label=\"Grid view\" @click=\"emit('update:modelValue', 'grid')\" />\n </div>\n</template>\n"});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/* Icon tiles download their glyph as a standalone SVG file. */
|
|
2
|
+
(function(){
|
|
3
|
+
function save(name, text){
|
|
4
|
+
var b = new Blob([text], {type:'image/svg+xml;charset=utf-8'});
|
|
5
|
+
var u = URL.createObjectURL(b);
|
|
6
|
+
var a = document.createElement('a');
|
|
7
|
+
a.href = u; a.download = name + '.svg';
|
|
8
|
+
document.body.appendChild(a); a.click(); a.remove();
|
|
9
|
+
setTimeout(function(){ URL.revokeObjectURL(u); }, 2000);
|
|
10
|
+
}
|
|
11
|
+
window.KBSaveSVG = function(name, inner){
|
|
12
|
+
save(name, '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">' + inner + '</svg>\n');
|
|
13
|
+
};
|
|
14
|
+
window.KBSaveRemoteSVG = function(name, url, onFail){
|
|
15
|
+
fetch(url).then(function(r){ if(!r.ok) throw 0; return r.text(); })
|
|
16
|
+
.then(function(t){ save(name, t); })
|
|
17
|
+
.catch(function(){ if(onFail) onFail(); });
|
|
18
|
+
};
|
|
19
|
+
})();
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
(function(){
|
|
2
|
+
var bundles = {};
|
|
3
|
+
/* Component source is loaded on demand, one script per folder (components/_shared/src-<dir>.js).
|
|
4
|
+
A <script> tag works from file:// and from any host; fetch() of a sibling source file does not. */
|
|
5
|
+
function loadBundle(dir, done){
|
|
6
|
+
if (!dir || bundles[dir] === 'done') return done();
|
|
7
|
+
if (bundles[dir]) { bundles[dir].push(done); return; }
|
|
8
|
+
bundles[dir] = [done];
|
|
9
|
+
var s = document.createElement('script');
|
|
10
|
+
s.src = '../_shared/src-' + dir + '.js';
|
|
11
|
+
s.onload = s.onerror = function(){
|
|
12
|
+
var q = bundles[dir]; bundles[dir] = 'done';
|
|
13
|
+
q.forEach(function(fn){ fn(); });
|
|
14
|
+
};
|
|
15
|
+
document.head.appendChild(s);
|
|
16
|
+
}
|
|
17
|
+
function esc(s){return s;}
|
|
18
|
+
function iconSvg(){return '<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m18 16 4-4-4-4"/><path d="m6 8-4 4 4 4"/><path d="m14.5 4-5 16"/></svg>';}
|
|
19
|
+
function copySvg(){return '<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>';}
|
|
20
|
+
function prettyHtml(html){
|
|
21
|
+
var s = html.replace(/></g,'>\n<');
|
|
22
|
+
var lines = s.split('\n'), indent=0, out=[];
|
|
23
|
+
for (var i=0;i<lines.length;i++){
|
|
24
|
+
var line = lines[i].trim(); if(!line) continue;
|
|
25
|
+
var closing = /^<\//.test(line);
|
|
26
|
+
if (closing) indent = Math.max(0, indent-1);
|
|
27
|
+
out.push(' '.repeat(indent) + line);
|
|
28
|
+
var selfClosing = /\/>$/.test(line) || /^<(br|img|input|hr)\b/i.test(line);
|
|
29
|
+
var isOpen = /^<[a-zA-Z]/.test(line) && !closing && !selfClosing;
|
|
30
|
+
if (isOpen) indent++;
|
|
31
|
+
}
|
|
32
|
+
return out.join('\n');
|
|
33
|
+
}
|
|
34
|
+
function resolveSrc(path, framework, cb){
|
|
35
|
+
var p = framework === 'vue' ? path.replace(/\.jsx$/, '.vue') : path;
|
|
36
|
+
var key = p.replace(/^\.\.\//, '');
|
|
37
|
+
function hit(){ return window.__KB_SRC__ && window.__KB_SRC__[key] != null ? window.__KB_SRC__[key] : null; }
|
|
38
|
+
var t = hit(); if (t != null) return cb(t);
|
|
39
|
+
loadBundle(key.split('/')[0], function(){
|
|
40
|
+
var t = hit(); if (t != null) return cb(t);
|
|
41
|
+
fetch(p).then(function(r){ if (!r.ok) throw 0; return r.text(); }).then(cb)
|
|
42
|
+
.catch(function(){ cb('// ' + p + ' has no standalone source file in this export.\n// It is defined inside _ds_bundle.js — the live demo above is the real component.'); });
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
function serve(files){
|
|
46
|
+
var framed; try { framed = window.parent !== window; } catch(e){ framed = true; }
|
|
47
|
+
if (!framed) return false;
|
|
48
|
+
var list = files.map(function(f){ return { name: f.name, path: f.path }; });
|
|
49
|
+
window.addEventListener('message', function(e){
|
|
50
|
+
var m = e.data; if (!m) return;
|
|
51
|
+
if (m.kb === 'src-get') {
|
|
52
|
+
resolveSrc(m.path, m.framework, function(text){
|
|
53
|
+
try { window.parent.postMessage({ kb:'src-text', path:m.path, framework:m.framework, text:text }, '*'); } catch(err){}
|
|
54
|
+
});
|
|
55
|
+
} else if (m.kb === 'theme' && m.theme) {
|
|
56
|
+
document.documentElement.setAttribute('data-theme', m.theme);
|
|
57
|
+
document.documentElement.style.colorScheme = m.theme;
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
function send(){ try { window.parent.postMessage({ kb:'src-manifest', files:list }, '*'); } catch(e){} }
|
|
61
|
+
send(); setTimeout(send, 250); setTimeout(send, 900);
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
function mount(rootSelector, files){
|
|
65
|
+
var root = document.querySelector(rootSelector);
|
|
66
|
+
if (!root || !root.parentNode) return;
|
|
67
|
+
/* Inside the design-system site the host page renders the View Code UI below the
|
|
68
|
+
component box, so the card only serves source text over postMessage. This path
|
|
69
|
+
also works from file://, where the host cannot touch this document at all. */
|
|
70
|
+
if (serve(files)) return;
|
|
71
|
+
var wrap = document.createElement('div');
|
|
72
|
+
wrap.setAttribute('data-kb-viewsource','1');
|
|
73
|
+
wrap.style.cssText = 'margin:16px 0 4px;font-family:var(--font-sans,sans-serif)';
|
|
74
|
+
var btn = document.createElement('button');
|
|
75
|
+
btn.type = 'button';
|
|
76
|
+
btn.innerHTML = iconSvg() + '<span style="margin-left:6px">View Code</span>';
|
|
77
|
+
btn.style.cssText = 'display:inline-flex;align-items:center;height:28px;padding:0 10px;border-radius:8px;border:1px solid var(--border-control,#e5e5e5);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:12px;font-family:var(--font-sans,sans-serif);cursor:pointer;';
|
|
78
|
+
var panel = document.createElement('div');
|
|
79
|
+
panel.style.cssText = 'display:none;margin-top:10px;border:1px solid var(--border-control,#e5e5e5);border-radius:8px;overflow:hidden;background:var(--bg-inset,#fafafa);';
|
|
80
|
+
var headRow = document.createElement('div');
|
|
81
|
+
headRow.style.cssText = 'display:flex;align-items:center;justify-content:space-between;background:var(--bg-surface,#fff);border-bottom:1px solid var(--border-subtle,#f0f0f0);';
|
|
82
|
+
var tabBar = document.createElement('div');
|
|
83
|
+
tabBar.style.cssText = 'display:flex;overflow-x:auto;flex:1;';
|
|
84
|
+
var fwBar = document.createElement('div');
|
|
85
|
+
fwBar.style.cssText = 'display:flex;gap:2px;padding:6px 8px;flex-shrink:0;';
|
|
86
|
+
var framework = 'react';
|
|
87
|
+
['React','Vue 3'].forEach(function(fw){
|
|
88
|
+
var fb = document.createElement('button');
|
|
89
|
+
fb.type = 'button'; fb.textContent = fw;
|
|
90
|
+
fb.style.cssText = 'font-size:11px;font-family:var(--font-sans,sans-serif);padding:4px 9px;border-radius:6px;border:1px solid var(--border-control,#e5e5e5);background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);cursor:pointer;';
|
|
91
|
+
fb.onclick = function(){
|
|
92
|
+
framework = fw === 'Vue 3' ? 'vue' : 'react';
|
|
93
|
+
Array.prototype.forEach.call(fwBar.children, function(el){
|
|
94
|
+
var on = el.textContent === fw;
|
|
95
|
+
el.style.background = on ? 'var(--action-primary,#155dfc)' : 'var(--bg-surface,#fff)';
|
|
96
|
+
el.style.color = on ? 'var(--on-accent,#fff)' : 'var(--text-secondary,#737373)';
|
|
97
|
+
el.style.borderColor = on ? 'var(--action-primary,#155dfc)' : 'var(--border-control,#e5e5e5)';
|
|
98
|
+
});
|
|
99
|
+
Array.prototype.forEach.call(tabBar.children, function(el, i){
|
|
100
|
+
if (tabs[i].html) return;
|
|
101
|
+
el.textContent = framework === 'vue' ? tabs[i].name.replace(/\.jsx$/, '.vue') : tabs[i].name;
|
|
102
|
+
});
|
|
103
|
+
if (active !== -1) selectTab(active);
|
|
104
|
+
};
|
|
105
|
+
fwBar.appendChild(fb);
|
|
106
|
+
});
|
|
107
|
+
fwBar.firstChild.style.background = 'var(--action-primary,#155dfc)';
|
|
108
|
+
fwBar.firstChild.style.color = 'var(--on-accent,#fff)';
|
|
109
|
+
fwBar.firstChild.style.borderColor = 'var(--action-primary,#155dfc)';
|
|
110
|
+
headRow.appendChild(tabBar); headRow.appendChild(fwBar);
|
|
111
|
+
var body = document.createElement('div');
|
|
112
|
+
var tabs = files.slice();
|
|
113
|
+
var cache = {}, active = -1;
|
|
114
|
+
function renderTab(t){
|
|
115
|
+
var box = document.createElement('div');
|
|
116
|
+
box.style.position = 'relative';
|
|
117
|
+
var pre = document.createElement('pre');
|
|
118
|
+
pre.style.cssText = 'margin:0;padding:14px 44px 14px 16px;overflow:auto;max-height:380px;font-family:var(--font-mono,monospace);font-size:11px;line-height:16.5px;color:var(--text-body,#525252);white-space:pre;';
|
|
119
|
+
var copyBtn = document.createElement('button');
|
|
120
|
+
copyBtn.type = 'button';
|
|
121
|
+
copyBtn.innerHTML = copySvg() + '<span style="margin-left:4px">Copy</span>';
|
|
122
|
+
copyBtn.style.cssText = 'position:absolute;top:8px;right:10px;display:inline-flex;align-items:center;height:22px;padding:0 8px;border-radius:6px;background:var(--bg-surface,#fff);color:var(--text-secondary,#737373);font-size:11px;font-family:var(--font-sans,sans-serif);cursor:pointer;border:1px solid var(--border-control,#e5e5e5);';
|
|
123
|
+
function setText(text){
|
|
124
|
+
pre.textContent = text;
|
|
125
|
+
copyBtn.onclick = function(){
|
|
126
|
+
try { navigator.clipboard.writeText(text); } catch(e){}
|
|
127
|
+
var old = copyBtn.innerHTML;
|
|
128
|
+
copyBtn.innerHTML = 'Copied';
|
|
129
|
+
setTimeout(function(){ copyBtn.innerHTML = old; }, 1200);
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
{
|
|
133
|
+
var p = framework === 'vue' ? t.path.replace(/\.jsx$/, '.vue') : t.path;
|
|
134
|
+
var key = p.replace(/^\.\.\//, '');
|
|
135
|
+
var fromCache = function(){
|
|
136
|
+
if (window.__KB_SRC__ && window.__KB_SRC__[key] != null) { setText(window.__KB_SRC__[key]); return true; }
|
|
137
|
+
return false;
|
|
138
|
+
};
|
|
139
|
+
if (!fromCache()) {
|
|
140
|
+
pre.textContent = 'Loading…';
|
|
141
|
+
// Sources are shipped as one script per folder so this works over file:// too,
|
|
142
|
+
// where fetch() to a sibling file is blocked. fetch is only the last resort.
|
|
143
|
+
loadBundle(key.split('/')[0], function(){
|
|
144
|
+
if (fromCache()) return;
|
|
145
|
+
fetch(p).then(function(r){ if (!r.ok) throw 0; return r.text(); })
|
|
146
|
+
.then(setText)
|
|
147
|
+
.catch(function(){ pre.textContent = '// ' + p + ' has no standalone source file in this export.\n// It is defined inside _ds_bundle.js — the live demo above is the real component.'; });
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
box.appendChild(pre); box.appendChild(copyBtn);
|
|
152
|
+
return box;
|
|
153
|
+
}
|
|
154
|
+
function selectTab(idx){
|
|
155
|
+
active = idx;
|
|
156
|
+
Array.prototype.forEach.call(tabBar.children, function(el, i){
|
|
157
|
+
el.style.color = i === idx ? 'var(--text-heading,#171717)' : 'var(--text-secondary,#737373)';
|
|
158
|
+
el.style.borderBottom = i === idx ? '2px solid var(--action-primary,#155dfc)' : '2px solid transparent';
|
|
159
|
+
});
|
|
160
|
+
body.innerHTML = '';
|
|
161
|
+
body.appendChild(renderTab(tabs[idx]));
|
|
162
|
+
}
|
|
163
|
+
tabs.forEach(function(t, i){
|
|
164
|
+
var tb = document.createElement('button');
|
|
165
|
+
tb.type = 'button';
|
|
166
|
+
tb.textContent = t.name;
|
|
167
|
+
tb.style.cssText = 'padding:8px 12px;border:none;background:transparent;font-size:12px;font-family:var(--font-sans,sans-serif);cursor:pointer;white-space:nowrap;margin-bottom:-1px;border-bottom:2px solid transparent;';
|
|
168
|
+
tb.onclick = function(){ selectTab(i); };
|
|
169
|
+
tabBar.appendChild(tb);
|
|
170
|
+
});
|
|
171
|
+
panel.appendChild(headRow);
|
|
172
|
+
panel.appendChild(body);
|
|
173
|
+
var open = false;
|
|
174
|
+
btn.onclick = function(){
|
|
175
|
+
open = !open;
|
|
176
|
+
panel.style.display = open ? 'block' : 'none';
|
|
177
|
+
btn.querySelector('span').textContent = open ? 'Hide Code' : 'View Code';
|
|
178
|
+
if (open && active === -1) selectTab(0);
|
|
179
|
+
};
|
|
180
|
+
wrap.appendChild(btn);
|
|
181
|
+
wrap.appendChild(panel);
|
|
182
|
+
root.parentNode.appendChild(wrap);
|
|
183
|
+
}
|
|
184
|
+
window.KBViewSource = { mount: mount };
|
|
185
|
+
})();
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/* Generated barrel — do not edit by hand. Run: node scripts/build-index.mjs
|
|
2
|
+
Vue 3 single-file components; your bundler needs @vitejs/plugin-vue or vue-loader. */
|
|
3
|
+
|
|
4
|
+
/* brand/ */
|
|
5
|
+
export { default as BrandVisual } from "../brand/BrandVisual.vue";
|
|
6
|
+
|
|
7
|
+
/* buttons/ */
|
|
8
|
+
export { default as ActionChip } from "../buttons/ActionChip.vue";
|
|
9
|
+
export { default as Button } from "../buttons/Button.vue";
|
|
10
|
+
export { default as IconButton } from "../buttons/IconButton.vue";
|
|
11
|
+
|
|
12
|
+
/* charts/ */
|
|
13
|
+
export { default as Bar3D } from "../charts/Bar3D.vue";
|
|
14
|
+
export { default as BarChart } from "../charts/BarChart.vue";
|
|
15
|
+
export { default as BarList } from "../charts/BarList.vue";
|
|
16
|
+
export { default as BoxPlot } from "../charts/BoxPlot.vue";
|
|
17
|
+
export { default as BulletChart } from "../charts/BulletChart.vue";
|
|
18
|
+
export { default as CandlestickChart } from "../charts/CandlestickChart.vue";
|
|
19
|
+
export { default as ChartKit } from "../charts/ChartKit.vue";
|
|
20
|
+
export { default as ChordChart } from "../charts/ChordChart.vue";
|
|
21
|
+
export { default as DonutChart } from "../charts/DonutChart.vue";
|
|
22
|
+
export { default as FunnelChart } from "../charts/FunnelChart.vue";
|
|
23
|
+
export { default as GanttChart } from "../charts/GanttChart.vue";
|
|
24
|
+
export { default as GaugeChart } from "../charts/GaugeChart.vue";
|
|
25
|
+
export { default as HeatmapChart } from "../charts/HeatmapChart.vue";
|
|
26
|
+
export { default as Histogram } from "../charts/Histogram.vue";
|
|
27
|
+
export { default as LineChart } from "../charts/LineChart.vue";
|
|
28
|
+
export { default as ParetoChart } from "../charts/ParetoChart.vue";
|
|
29
|
+
export { default as Pie3D } from "../charts/Pie3D.vue";
|
|
30
|
+
export { default as RadarChart } from "../charts/RadarChart.vue";
|
|
31
|
+
export { default as Ribbon3D } from "../charts/Ribbon3D.vue";
|
|
32
|
+
export { default as SankeyChart } from "../charts/SankeyChart.vue";
|
|
33
|
+
export { default as Scatter3D } from "../charts/Scatter3D.vue";
|
|
34
|
+
export { default as ScatterChart } from "../charts/ScatterChart.vue";
|
|
35
|
+
export { default as Sparkline } from "../charts/Sparkline.vue";
|
|
36
|
+
export { default as SunburstChart } from "../charts/SunburstChart.vue";
|
|
37
|
+
export { default as Surface3D } from "../charts/Surface3D.vue";
|
|
38
|
+
export { default as TreemapChart } from "../charts/TreemapChart.vue";
|
|
39
|
+
export { default as WaterfallChart } from "../charts/WaterfallChart.vue";
|
|
40
|
+
export { default as WorldMap } from "../charts/WorldMap.vue";
|
|
41
|
+
|
|
42
|
+
/* data/ */
|
|
43
|
+
export { default as CodeBlock } from "../data/CodeBlock.vue";
|
|
44
|
+
export { default as DataTable } from "../data/DataTable.vue";
|
|
45
|
+
export { default as DiffValue } from "../data/DiffValue.vue";
|
|
46
|
+
export { default as FilterBar } from "../data/FilterBar.vue";
|
|
47
|
+
export { default as KeyValue } from "../data/KeyValue.vue";
|
|
48
|
+
export { default as ListRow } from "../data/ListRow.vue";
|
|
49
|
+
export { default as Pagination } from "../data/Pagination.vue";
|
|
50
|
+
export { default as SmartTable } from "../data/SmartTable.vue";
|
|
51
|
+
export { default as Spreadsheet } from "../data/Spreadsheet.vue";
|
|
52
|
+
|
|
53
|
+
/* display/ */
|
|
54
|
+
export { default as Avatar } from "../display/Avatar.vue";
|
|
55
|
+
export { default as Badge } from "../display/Badge.vue";
|
|
56
|
+
export { default as Card } from "../display/Card.vue";
|
|
57
|
+
export { default as Divider } from "../display/Divider.vue";
|
|
58
|
+
export { default as Kbd } from "../display/Kbd.vue";
|
|
59
|
+
export { default as ProgressBar } from "../display/ProgressBar.vue";
|
|
60
|
+
export { default as Tag } from "../display/Tag.vue";
|
|
61
|
+
|
|
62
|
+
/* feedback/ */
|
|
63
|
+
export { default as Accordion } from "../feedback/Accordion.vue";
|
|
64
|
+
export { default as ActivityItem } from "../feedback/ActivityItem.vue";
|
|
65
|
+
export { default as Alert } from "../feedback/Alert.vue";
|
|
66
|
+
export { default as Banner } from "../feedback/Banner.vue";
|
|
67
|
+
export { default as ContextMenu } from "../feedback/ContextMenu.vue";
|
|
68
|
+
export { default as Drawer } from "../feedback/Drawer.vue";
|
|
69
|
+
export { default as Menu } from "../feedback/Menu.vue";
|
|
70
|
+
export { default as Modal } from "../feedback/Modal.vue";
|
|
71
|
+
export { default as Stepper } from "../feedback/Stepper.vue";
|
|
72
|
+
export { default as Timeline } from "../feedback/Timeline.vue";
|
|
73
|
+
|
|
74
|
+
/* forms/ */
|
|
75
|
+
export { default as Checkbox } from "../forms/Checkbox.vue";
|
|
76
|
+
export { default as Combobox } from "../forms/Combobox.vue";
|
|
77
|
+
export { default as DatePicker } from "../forms/DatePicker.vue";
|
|
78
|
+
export { default as Dropzone } from "../forms/Dropzone.vue";
|
|
79
|
+
export { default as Field } from "../forms/Field.vue";
|
|
80
|
+
export { default as Input } from "../forms/Input.vue";
|
|
81
|
+
export { default as MultiSelect } from "../forms/MultiSelect.vue";
|
|
82
|
+
export { default as NumberStepper } from "../forms/NumberStepper.vue";
|
|
83
|
+
export { default as Radio } from "../forms/Radio.vue";
|
|
84
|
+
export { default as RadioGroup } from "../forms/RadioGroup.vue";
|
|
85
|
+
export { default as SearchInput } from "../forms/SearchInput.vue";
|
|
86
|
+
export { default as Select } from "../forms/Select.vue";
|
|
87
|
+
export { default as Slider } from "../forms/Slider.vue";
|
|
88
|
+
export { default as Switch } from "../forms/Switch.vue";
|
|
89
|
+
|
|
90
|
+
/* icons/ */
|
|
91
|
+
export { default as Icon } from "../icons/Icon.vue";
|
|
92
|
+
export { default as Unicon } from "../icons/Unicon.vue";
|
|
93
|
+
|
|
94
|
+
/* navigation/ */
|
|
95
|
+
export { default as Breadcrumb } from "../navigation/Breadcrumb.vue";
|
|
96
|
+
export { default as DocTree } from "../navigation/DocTree.vue";
|
|
97
|
+
export { default as Sidebar } from "../navigation/Sidebar.vue";
|
|
98
|
+
export { default as TopBar } from "../navigation/TopBar.vue";
|
|
99
|
+
export { default as Tree } from "../navigation/Tree.vue";
|
|
100
|
+
|
|
101
|
+
/* pipeline/ */
|
|
102
|
+
export { default as ConfigPanel } from "../pipeline/ConfigPanel.vue";
|
|
103
|
+
export { default as DataPreview } from "../pipeline/DataPreview.vue";
|
|
104
|
+
export { default as NodeCard } from "../pipeline/NodeCard.vue";
|
|
105
|
+
export { default as PaletteItem } from "../pipeline/PaletteItem.vue";
|
|
106
|
+
export { default as PaletteListItem } from "../pipeline/PaletteListItem.vue";
|
|
107
|
+
export { default as PalettePanel } from "../pipeline/PalettePanel.vue";
|
|
108
|
+
export { default as PipelineCanvas } from "../pipeline/PipelineCanvas.vue";
|
|
109
|
+
export { default as ZoomControls } from "../pipeline/ZoomControls.vue";
|
|
110
|
+
|
|
111
|
+
/* tabs/ */
|
|
112
|
+
export { default as SegmentedControl } from "../tabs/SegmentedControl.vue";
|
|
113
|
+
export { default as Tabs } from "../tabs/Tabs.vue";
|
|
114
|
+
export { default as ViewToggle } from "../tabs/ViewToggle.vue";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
/** Generative brand visual — dark, geometric, always on-brand. Seven motifs, one 1200×400 canvas each, cropped to fill any box. */
|
|
3
|
+
export type BrandMotif = "orbit" | "entropy" | "focal" | "manifold" | "tesseract" | "meridian" | "ledger";
|
|
4
|
+
export interface BrandVisualProps {
|
|
5
|
+
/** Which motif to draw. Banner-leaning: orbit, entropy, ledger. Centred: focal, manifold, tesseract, meridian. */
|
|
6
|
+
motif?: BrandMotif;
|
|
7
|
+
width?: number | string;
|
|
8
|
+
height?: number | string;
|
|
9
|
+
/** Corner radius; pass 0 for a full-bleed hero. */
|
|
10
|
+
radius?: number | string;
|
|
11
|
+
/** "slice" crops to fill (default); "meet" letterboxes the whole canvas. */
|
|
12
|
+
fit?: "slice" | "meet";
|
|
13
|
+
/** Overlay content — headings, eyebrows. Sits above the artwork. */
|
|
14
|
+
children?: React.ReactNode;
|
|
15
|
+
style?: React.CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
export declare function BrandVisual(props: BrandVisualProps): JSX.Element;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { buildMotif, motifList, motifLabels } from "./brandMotifs.js";
|
|
3
|
+
|
|
4
|
+
/** Generative brand visual. One canvas (1200×400) per motif, cropped to fill any box —
|
|
5
|
+
* use for page heroes, section headers and card headers. Always dark; never themed. */
|
|
6
|
+
export function BrandVisual({ motif = "orbit", width = "100%", height = 200, radius = "var(--radius-lg)", fit = "slice", children, style }) {
|
|
7
|
+
const { viewBox, els } = buildMotif(motif, 1200, 400);
|
|
8
|
+
return (
|
|
9
|
+
<div style={{ position: "relative", width, height, borderRadius: radius, overflow: "hidden", background: "#060a1e", ...style }}>
|
|
10
|
+
<svg viewBox={viewBox} preserveAspectRatio={fit === "slice" ? "xMidYMid slice" : "xMidYMid meet"} style={{ position: "absolute", inset: 0, width: "100%", height: "100%", display: "block" }}>
|
|
11
|
+
<defs>
|
|
12
|
+
<linearGradient id="kbv-bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stopColor="#060a1e" /><stop offset=".5" stopColor="#0b1638" /><stop offset="1" stopColor="#10265f" /></linearGradient>
|
|
13
|
+
<linearGradient id="kbv-bg-v" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor="#060a1e" /><stop offset=".55" stopColor="#0b1638" /><stop offset="1" stopColor="#10265f" /></linearGradient>
|
|
14
|
+
<linearGradient id="kbv-bg-d" x1="1" y1="0" x2="0" y2="1"><stop offset="0" stopColor="#0a1231" /><stop offset=".55" stopColor="#070c22" /><stop offset="1" stopColor="#050815" /></linearGradient>
|
|
15
|
+
<radialGradient id="kbv-core" cx=".5" cy=".5" r=".5"><stop offset="0" stopColor="#2b7fff" stopOpacity=".55" /><stop offset="1" stopColor="#2b7fff" stopOpacity="0" /></radialGradient>
|
|
16
|
+
<radialGradient id="kbv-glow" cx=".5" cy=".5" r=".5"><stop offset="0" stopColor="#155dfc" stopOpacity=".5" /><stop offset="1" stopColor="#155dfc" stopOpacity="0" /></radialGradient>
|
|
17
|
+
<filter id="kbv-b1"><feGaussianBlur stdDeviation="1.5" /></filter>
|
|
18
|
+
<filter id="kbv-b2"><feGaussianBlur stdDeviation="3" /></filter>
|
|
19
|
+
<filter id="kbv-b3"><feGaussianBlur stdDeviation="9" /></filter>
|
|
20
|
+
</defs>
|
|
21
|
+
{els.map(({ t, ...a }, i) => React.createElement(t, { key: i, ...a }))}
|
|
22
|
+
</svg>
|
|
23
|
+
{children ? <div style={{ position: "relative", height: "100%" }}>{children}</div> : null}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
BrandVisual.motifs = motifList;
|
|
29
|
+
BrandVisual.labels = motifLabels;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<script setup>
|
|
2
|
+
// Generative brand visual. One canvas (1200×400) per motif, cropped to fill any box. Always dark; never themed.
|
|
3
|
+
import { computed } from 'vue'
|
|
4
|
+
import { buildMotif } from './brandMotifs.js'
|
|
5
|
+
|
|
6
|
+
const props = defineProps({
|
|
7
|
+
motif: { type: String, default: 'orbit' },
|
|
8
|
+
width: { type: [Number, String], default: '100%' },
|
|
9
|
+
height: { type: [Number, String], default: 200 },
|
|
10
|
+
radius: { type: [Number, String], default: 'var(--radius-lg)' },
|
|
11
|
+
fit: { type: String, default: 'slice' },
|
|
12
|
+
style: Object
|
|
13
|
+
})
|
|
14
|
+
const art = computed(() => buildMotif(props.motif, 1200, 400))
|
|
15
|
+
const attrsOf = (e) => { const { t, ...a } = e; return a }
|
|
16
|
+
</script>
|
|
17
|
+
<template>
|
|
18
|
+
<div :style="{ position:'relative', width: props.width, height: props.height, borderRadius: props.radius, overflow:'hidden', background:'#060a1e', ...props.style }">
|
|
19
|
+
<svg :viewBox="art.viewBox" :preserveAspectRatio="props.fit === 'slice' ? 'xMidYMid slice' : 'xMidYMid meet'" style="position:absolute;inset:0;width:100%;height:100%;display:block">
|
|
20
|
+
<defs>
|
|
21
|
+
<linearGradient id="kbv-bg" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#060a1e" /><stop offset=".5" stop-color="#0b1638" /><stop offset="1" stop-color="#10265f" /></linearGradient>
|
|
22
|
+
<linearGradient id="kbv-bg-v" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#060a1e" /><stop offset=".55" stop-color="#0b1638" /><stop offset="1" stop-color="#10265f" /></linearGradient>
|
|
23
|
+
<linearGradient id="kbv-bg-d" x1="1" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#0a1231" /><stop offset=".55" stop-color="#070c22" /><stop offset="1" stop-color="#050815" /></linearGradient>
|
|
24
|
+
<radialGradient id="kbv-core" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#2b7fff" stop-opacity=".55" /><stop offset="1" stop-color="#2b7fff" stop-opacity="0" /></radialGradient>
|
|
25
|
+
<radialGradient id="kbv-glow" cx=".5" cy=".5" r=".5"><stop offset="0" stop-color="#155dfc" stop-opacity=".5" /><stop offset="1" stop-color="#155dfc" stop-opacity="0" /></radialGradient>
|
|
26
|
+
<filter id="kbv-b1"><feGaussianBlur stdDeviation="1.5" /></filter>
|
|
27
|
+
<filter id="kbv-b2"><feGaussianBlur stdDeviation="3" /></filter>
|
|
28
|
+
<filter id="kbv-b3"><feGaussianBlur stdDeviation="9" /></filter>
|
|
29
|
+
</defs>
|
|
30
|
+
<component v-for="(e, i) in art.els" :key="i" :is="e.t" v-bind="attrsOf(e)" />
|
|
31
|
+
</svg>
|
|
32
|
+
<div v-if="$slots.default" style="position:relative;height:100%"><slot /></div>
|
|
33
|
+
</div>
|
|
34
|
+
</template>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!-- @dsCard group="Directory Index" viewport="700x120" name="Brand dir" subtitle="Full card: Brand → Brand Visuals" -->
|
|
2
|
+
<!DOCTYPE html>
|
|
3
|
+
<html><head><meta charset="utf-8"><link rel="stylesheet" href="../../styles.css">
|
|
4
|
+
<script src="https://unpkg.com/react@18.3.1/umd/react.development.js" integrity="sha384-hD6/rw4ppMLGNu3tX5cjIb+uRZ7UkRJ6BPkLpg4hAu/6onKUg4lLsHAs9EBPT82L" crossorigin="anonymous"></script>
|
|
5
|
+
<script src="https://unpkg.com/react-dom@18.3.1/umd/react-dom.development.js" integrity="sha384-u6aeetuaXnQ38mYT8rp6sbXaQe3NL9t+IBXmnYxwkUI2Hw4bsp2Wvmx4yRQF1uAm" crossorigin="anonymous"></script>
|
|
6
|
+
<script src="https://unpkg.com/@babel/standalone@7.29.0/babel.min.js" integrity="sha384-m08KidiNqLdpJqLq95G/LEi8Qvjl/xUYll3QILypMoQ65QorJ9Lvtp2RXYGBFj1y" crossorigin="anonymous"></script>
|
|
7
|
+
<script src="../../_ds_bundle.js"></script>
|
|
8
|
+
<style>body{margin:0;background:var(--bg-surface);padding:16px;font-family:var(--font-sans)}</style></head>
|
|
9
|
+
<body><div id="root"></div>
|
|
10
|
+
<script type="text/babel">
|
|
11
|
+
const { BrandVisual } = window.KeybricksDesignSystem_71231a;
|
|
12
|
+
ReactDOM.createRoot(document.getElementById("root")).render(<BrandVisual motif="orbit" height={88}/>);
|
|
13
|
+
</script></body></html>
|