@keboola/design 17.0.0 → 17.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -1
- package/dist/{AIKit-CxKkvN0q.js → AIKit-DiF0LtVr.js} +22 -14
- package/dist/AIKit-DiF0LtVr.js.map +1 -0
- package/dist/{Alert-DziS8n1H.js → Alert-BQB-og_I.js} +2 -2
- package/dist/{Alert-DziS8n1H.js.map → Alert-BQB-og_I.js.map} +1 -1
- package/dist/{AlertModal-De04fFoy.js → AlertModal-BkmyEvsR.js} +2 -2
- package/dist/{AlertModal-De04fFoy.js.map → AlertModal-BkmyEvsR.js.map} +1 -1
- package/dist/{AppShell-BUa3U7-e.js → AppShell-CDbecT-t.js} +2 -2
- package/dist/{AppShell-BUa3U7-e.js.map → AppShell-CDbecT-t.js.map} +1 -1
- package/dist/{BrandEditor-hiLBX7az.js → BrandEditor-BHE6nsob.js} +6 -6
- package/dist/{BrandEditor-hiLBX7az.js.map → BrandEditor-BHE6nsob.js.map} +1 -1
- package/dist/{BrandLogo-Dn7hhp2c.js → BrandLogo-n4Qhi1tI.js} +2 -2
- package/dist/{BrandLogo-Dn7hhp2c.js.map → BrandLogo-n4Qhi1tI.js.map} +1 -1
- package/dist/{Breadcrumb-uGFEIjFF.js → Breadcrumb-BAwQTo0c.js} +3 -3
- package/dist/{Breadcrumb-uGFEIjFF.js.map → Breadcrumb-BAwQTo0c.js.map} +1 -1
- package/dist/{Button-DbvGzEDb.js → Button-BAMX1iuI.js} +2 -2
- package/dist/{Button-DbvGzEDb.js.map → Button-BAMX1iuI.js.map} +1 -1
- package/dist/{Checkbox-B2jLlyaO.js → Checkbox-CfjpwIcg.js} +2 -2
- package/dist/{Checkbox-B2jLlyaO.js.map → Checkbox-CfjpwIcg.js.map} +1 -1
- package/dist/{Clipboard-fAkDdpEu.js → Clipboard-BaU3cDi5.js} +7 -5
- package/dist/Clipboard-BaU3cDi5.js.map +1 -0
- package/dist/{ClipboardButton-71Ugbl8c.js → ClipboardButton-BWeaQEM2.js} +5 -4
- package/dist/ClipboardButton-BWeaQEM2.js.map +1 -0
- package/dist/{CodeEditor-D_b-ZCng.js → CodeEditor-BdTxkPD4.js} +4 -4
- package/dist/{CodeEditor-D_b-ZCng.js.map → CodeEditor-BdTxkPD4.js.map} +1 -1
- package/dist/{ColorInput-Cjr1jkvy.js → ColorInput-BoVdBxJ7.js} +3 -3
- package/dist/{ColorInput-Cjr1jkvy.js.map → ColorInput-BoVdBxJ7.js.map} +1 -1
- package/dist/{ColorModeSwitch-CuPYnJ2V.js → ColorModeSwitch-BDkdmHYl.js} +2 -2
- package/dist/{ColorModeSwitch-CuPYnJ2V.js.map → ColorModeSwitch-BDkdmHYl.js.map} +1 -1
- package/dist/{Combobox-DYjvYgl3.js → Combobox-CakN_1cF.js} +6 -6
- package/dist/Combobox-CakN_1cF.js.map +1 -0
- package/dist/{Command-BcTb6GsR.js → Command-D6XYzzJf.js} +2 -2
- package/dist/{Command-BcTb6GsR.js.map → Command-D6XYzzJf.js.map} +1 -1
- package/dist/{CopyField-CnwAvQz9.js → CopyField-9WBU8thS.js} +2 -2
- package/dist/{CopyField-CnwAvQz9.js.map → CopyField-9WBU8thS.js.map} +1 -1
- package/dist/{DateInput-DJNX7Vdx.js → DateInput-CCYG0AtT.js} +3 -3
- package/dist/{DateInput-DJNX7Vdx.js.map → DateInput-CCYG0AtT.js.map} +1 -1
- package/dist/{DateRangeInput-B3g8cyS0.js → DateRangeInput-CpZK1IFp.js} +3 -3
- package/dist/{DateRangeInput-B3g8cyS0.js.map → DateRangeInput-CpZK1IFp.js.map} +1 -1
- package/dist/{DateRangePicker-48yOB2qV.js → DateRangePicker-B2AUKX7u.js} +2 -2
- package/dist/{DateRangePicker-48yOB2qV.js.map → DateRangePicker-B2AUKX7u.js.map} +1 -1
- package/dist/{FieldItem-D7eCMfMB.js → FieldItem-eM7IHBt5.js} +3 -3
- package/dist/{FieldItem-D7eCMfMB.js.map → FieldItem-eM7IHBt5.js.map} +1 -1
- package/dist/{FileUpload-DskvxhAr.js → FileUpload-d-hNPfPV.js} +2 -2
- package/dist/{FileUpload-DskvxhAr.js.map → FileUpload-d-hNPfPV.js.map} +1 -1
- package/dist/{Form-BUNW7nlt.js → Form-B_9sNehB.js} +7 -7
- package/dist/{Form-BUNW7nlt.js.map → Form-B_9sNehB.js.map} +1 -1
- package/dist/{FormGroup-QJB21LKp.js → FormGroup-D7ahkK1q.js} +3 -3
- package/dist/{FormGroup-QJB21LKp.js.map → FormGroup-D7ahkK1q.js.map} +1 -1
- package/dist/{FullScreenModal-RzY5XxiG.js → FullScreenModal-CiRKNsls.js} +2 -2
- package/dist/{FullScreenModal-RzY5XxiG.js.map → FullScreenModal-CiRKNsls.js.map} +1 -1
- package/dist/{InlineEdit-CXL0ZslO.js → InlineEdit-BzOox46S.js} +4 -4
- package/dist/{InlineEdit-CXL0ZslO.js.map → InlineEdit-BzOox46S.js.map} +1 -1
- package/dist/{InputGroup-BWeanEo7.js → InputGroup-DnGP9-ZQ.js} +2 -2
- package/dist/{InputGroup-BWeanEo7.js.map → InputGroup-DnGP9-ZQ.js.map} +1 -1
- package/dist/{Kbd-BprYCICe.js → Kbd-6velz9J-.js} +2 -2
- package/dist/{Kbd-BprYCICe.js.map → Kbd-6velz9J-.js.map} +1 -1
- package/dist/{MermaidDiagram-E3azndPk.js → MermaidDiagram-l83gco3r.js} +2 -2
- package/dist/{MermaidDiagram-E3azndPk.js.map → MermaidDiagram-l83gco3r.js.map} +1 -1
- package/dist/{Modal-Cf_kPEBW.js → Modal-CLu6Eg5p.js} +2 -2
- package/dist/{Modal-Cf_kPEBW.js.map → Modal-CLu6Eg5p.js.map} +1 -1
- package/dist/{MultiActionButtonGroup-8WQwL4UQ.js → MultiActionButtonGroup-B3-wNw7w.js} +3 -3
- package/dist/{MultiActionButtonGroup-8WQwL4UQ.js.map → MultiActionButtonGroup-B3-wNw7w.js.map} +1 -1
- package/dist/{Navbar-CRjxVboE.js → Navbar-BEc-Pqo5.js} +2 -2
- package/dist/{Navbar-CRjxVboE.js.map → Navbar-BEc-Pqo5.js.map} +1 -1
- package/dist/{PageHeader-DosMyar2.js → PageHeader-CfFKjCzo.js} +2 -2
- package/dist/PageHeader-CfFKjCzo.js.map +1 -0
- package/dist/{PageToolbar-By2HznxO.js → PageToolbar-NmiausP7.js} +2 -2
- package/dist/{PageToolbar-By2HznxO.js.map → PageToolbar-NmiausP7.js.map} +1 -1
- package/dist/{Pagination-DrXTrvfD.js → Pagination-DidOPSPB.js} +2 -2
- package/dist/{Pagination-DrXTrvfD.js.map → Pagination-DidOPSPB.js.map} +1 -1
- package/dist/{Popconfirm-BJuDOHdv.js → Popconfirm-C4ryoeGP.js} +2 -2
- package/dist/{Popconfirm-BJuDOHdv.js.map → Popconfirm-C4ryoeGP.js.map} +1 -1
- package/dist/{RadioGroup-B9oC8P15.js → RadioGroup-BIElT-nH.js} +2 -2
- package/dist/{RadioGroup-B9oC8P15.js.map → RadioGroup-BIElT-nH.js.map} +1 -1
- package/dist/{Search-OgaNjqQM.js → Search-xDWaQ2z3.js} +2 -2
- package/dist/{Search-OgaNjqQM.js.map → Search-xDWaQ2z3.js.map} +1 -1
- package/dist/{SearchInput-eLS-uvOn.js → SearchInput-BwlJ77mV.js} +3 -3
- package/dist/{SearchInput-eLS-uvOn.js.map → SearchInput-BwlJ77mV.js.map} +1 -1
- package/dist/{SectionNav-L81iJBo3.js → SectionNav-DV3Hwj-5.js} +14 -8
- package/dist/SectionNav-DV3Hwj-5.js.map +1 -0
- package/dist/{Select-DRdkdzBp.js → Select-C3Q_Xk_Z.js} +2 -2
- package/dist/{Select-DRdkdzBp.js.map → Select-C3Q_Xk_Z.js.map} +1 -1
- package/dist/{Sheet-DVb4XoJ5.js → Sheet-D7NnOyud.js} +2 -2
- package/dist/{Sheet-DVb4XoJ5.js.map → Sheet-D7NnOyud.js.map} +1 -1
- package/dist/{Sidebar-DYvodJWN.js → Sidebar-DaUDM8jm.js} +5 -5
- package/dist/{Sidebar-DYvodJWN.js.map → Sidebar-DaUDM8jm.js.map} +1 -1
- package/dist/{Slider-B9IskDxf.js → Slider-Cdl8FOvI.js} +3 -3
- package/dist/{Slider-B9IskDxf.js.map → Slider-Cdl8FOvI.js.map} +1 -1
- package/dist/{Table-BwgBm3hJ.js → Table-DtLrXt4i.js} +39 -7
- package/dist/Table-DtLrXt4i.js.map +1 -0
- package/dist/{Tabs-DmJB4l_N.js → Tabs-DUOUQKh7.js} +2 -2
- package/dist/{Tabs-DmJB4l_N.js.map → Tabs-DUOUQKh7.js.map} +1 -1
- package/dist/{TextInput--1CfZybC.js → TextInput-BAL-zOAN.js} +3 -3
- package/dist/{TextInput--1CfZybC.js.map → TextInput-BAL-zOAN.js.map} +1 -1
- package/dist/{ThemeProvider-BCHllN7d.js → ThemeProvider-MzVbTDBj.js} +2 -2
- package/dist/{ThemeProvider-BCHllN7d.js.map → ThemeProvider-MzVbTDBj.js.map} +1 -1
- package/dist/{ToggleAllTooltip-BFmA6TXf.js → ToggleAllTooltip-DQE5Lw6w.js} +2 -2
- package/dist/{ToggleAllTooltip-BFmA6TXf.js.map → ToggleAllTooltip-DQE5Lw6w.js.map} +1 -1
- package/dist/{ZoomableSvg-BRPGgaWJ.js → ZoomableSvg-CIOqAYpd.js} +4 -4
- package/dist/{ZoomableSvg-BRPGgaWJ.js.map → ZoomableSvg-CIOqAYpd.js.map} +1 -1
- package/dist/editors.js +3 -3
- package/dist/flint.js +3 -3
- package/dist/flint.js.map +1 -1
- package/dist/index.d.ts +410 -354
- package/dist/index.js +52 -50
- package/dist/internal/blocks/AIKit.js +1 -1
- package/dist/internal/blocks/AppShell.js +1 -1
- package/dist/internal/blocks/AuthLayout.js +1 -1
- package/dist/internal/blocks/BrandEditor.js +1 -1
- package/dist/internal/blocks/BulkActionsBar.js +55 -0
- package/dist/internal/blocks/BulkActionsBar.js.map +1 -0
- package/dist/internal/blocks/ChipEditor.js +2 -2
- package/dist/internal/blocks/ClipboardButton.js +1 -1
- package/dist/internal/blocks/ColorInput.js +1 -1
- package/dist/internal/blocks/ColorModeSwitch.js +1 -1
- package/dist/internal/blocks/Command.js +1 -1
- package/dist/internal/blocks/CopyField.js +1 -1
- package/dist/internal/blocks/DateInput.js +1 -1
- package/dist/internal/blocks/DateRangeInput.js +1 -1
- package/dist/internal/blocks/ExportMenu.js +2 -2
- package/dist/internal/blocks/Form/connectors/ComboboxField.js +2 -2
- package/dist/internal/blocks/Form/connectors/MultiComboboxField.js +1 -1
- package/dist/internal/blocks/Form.js +2 -2
- package/dist/internal/blocks/FormGroup.js +1 -1
- package/dist/internal/blocks/InlineEdit.js +1 -1
- package/dist/internal/blocks/MultiActionButtonGroup.js +1 -1
- package/dist/internal/blocks/MultiCombobox.js +3 -3
- package/dist/internal/blocks/NumberInput.js +1 -1
- package/dist/internal/blocks/PageHeader.js +1 -1
- package/dist/internal/blocks/PageSection.js +31 -0
- package/dist/internal/blocks/PageSection.js.map +1 -0
- package/dist/internal/blocks/PageToolbar.js +1 -1
- package/dist/internal/blocks/PasswordInput.js +2 -2
- package/dist/internal/blocks/SearchInput.js +1 -1
- package/dist/internal/blocks/SearchInputWithNavigation.js +3 -3
- package/dist/internal/blocks/SectionNav.js +1 -1
- package/dist/internal/blocks/SplitButton.js +1 -1
- package/dist/internal/blocks/TextInput.js +1 -1
- package/dist/internal/blocks/Textarea.js +1 -1
- package/dist/internal/blocks/TimeInput.js +2 -2
- package/dist/internal/components/Alert.js +1 -1
- package/dist/internal/components/AlertModal.js +1 -1
- package/dist/internal/components/Avatar.js +3 -2
- package/dist/internal/components/Avatar.js.map +1 -1
- package/dist/internal/components/BrandLogo.js +1 -1
- package/dist/internal/components/Breadcrumb.js +1 -1
- package/dist/internal/components/Button.js +1 -1
- package/dist/internal/components/Checkbox.js +1 -1
- package/dist/internal/components/Clipboard.js +1 -1
- package/dist/internal/components/CollapsiblePanel.js +1 -1
- package/dist/internal/components/Combobox.js +1 -1
- package/dist/internal/components/DateRangePicker.js +1 -1
- package/dist/internal/components/FileUpload.js +1 -1
- package/dist/internal/components/FullScreenModal.js +1 -1
- package/dist/internal/components/InputGroup.js +1 -1
- package/dist/internal/components/Kbd.js +1 -1
- package/dist/internal/components/Modal.js +1 -1
- package/dist/internal/components/Navbar.js +1 -1
- package/dist/internal/components/Pagination.js +1 -1
- package/dist/internal/components/Popconfirm.js +1 -1
- package/dist/internal/components/RadioGroup.js +1 -1
- package/dist/internal/components/Search.js +1 -1
- package/dist/internal/components/Select.js +1 -1
- package/dist/internal/components/Sheet.js +1 -1
- package/dist/internal/components/Sidebar.js +1 -1
- package/dist/internal/components/Slider.js +1 -1
- package/dist/internal/components/Table.js +1 -1
- package/dist/internal/components/Tabs.js +1 -1
- package/dist/internal/components/ThemeProvider.js +1 -1
- package/dist/internal/components/Tooltip.js +1 -1
- package/dist/internal/components/Truncated.js +1 -1
- package/dist/internal/components/ZoomableSvg.js +1 -1
- package/dist/internal/utils.js +1 -1
- package/dist/markdown-editor.js +6 -6
- package/dist/mermaid.js +1 -1
- package/dist/rjsf.js +74 -54
- package/dist/rjsf.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing.js +1 -1
- package/dist/{utils-DjFceY3H.js → utils-BxEJOnFD.js} +2 -2
- package/dist/{utils-DjFceY3H.js.map → utils-BxEJOnFD.js.map} +1 -1
- package/package.json +3 -3
- package/src/styles.css +11 -1
- package/dist/AIKit-CxKkvN0q.js.map +0 -1
- package/dist/Clipboard-fAkDdpEu.js.map +0 -1
- package/dist/ClipboardButton-71Ugbl8c.js.map +0 -1
- package/dist/Combobox-DYjvYgl3.js.map +0 -1
- package/dist/PageHeader-DosMyar2.js.map +0 -1
- package/dist/SectionNav-L81iJBo3.js.map +0 -1
- package/dist/Table-BwgBm3hJ.js.map +0 -1
package/dist/flint.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, tierTwoColour } from '../../vega/vegaTokens';\n\n/**\n * Put the design system's `--chart-1..5` palette on a Flint input.\n *\n * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.\n * - An author-supplied `theme_spec` is passed through untouched.\n * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's defaults assume a light canvas, so text and structure come from tokens.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink on a filled (series-coloured) mark.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint swaps `categorical` for its theme-blind `tableau10`;\n // this keeps 6-10 on our hues (`tierTwoColour`).\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) => tierTwoColour(colour, scheme)),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // For a quantitative `color` field; flint's default starts at black, invisible on dark.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: nothing in a spec may fetch or run.\n *\n * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.\n * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - Via a descriptor: inherited keys are ignored and a getter is never called.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's rows (`spec.data.values` only), which the walk does not descend into.\n *\n * - Rows are data vega never evaluates, so a column named `url` must not be refused.\n * - Flint takes one top-level dataset, so every other node is spec.\n * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by column name, so their keys are data, not spec.\n *\n * - Only the keys are exempt; each value is walked like any other node.\n * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.\n * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.\n * - An accessor is refused, not skipped: its value cannot be read without running it.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors: skip inherited keys and never call a getter.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The lazily imported modules the pipeline needs; a parameter so tests can pass a fake runtime.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.\n * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.\n * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.\n * - Renders headless; `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n const assembled: Record<string, unknown> = runtime.assembleVegaLite(input);\n // `assembleVegaLite` is typed loosely upstream; without the hint keys it is a Vega-Lite spec.\n const vlSpec = Object.fromEntries(\n Object.entries(assembled).filter(([key]) => !key.startsWith('_')),\n ) as unknown as TopLevelSpec;\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { createSvgCache, diagramCacheKey } from '../DiagramShared/svgCache';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// Memoized chart-stack import, reset on failure so a chunk-load error can retry.\n// Mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// A streaming fence is unparseable just like a broken one, so unparseable source\n// keeps the skeleton until it has been unchanged this long, then shows as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Parse, compile and layout are synchronous, so a larger source would freeze the chat.\n// Far above the prompt's ~50-row inline data cap.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// Values are already `makeResponsive`d, so ZoomableSvg's sanitize memo stays stable.\nconst svgCache = createSvgCache();\n\n// The theme half of a cache key — tells a still-streaming spec from a theme switch.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */\n code: string;\n /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // - Re-renders on a scheme or brand change, same contract as MermaidDiagram.\n // - `scope` is the element tokens are read off (`null` = `<html>`); it is part of `themeKey`.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = diagramCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Reset stale state synchronously on a code or theme change (no one-frame flash):\n // 1. A cache hit is applied at once.\n // 2. Same theme, source only grew (still streaming): keep the last chart, no flicker.\n // 3. Anything else clears — colours are baked in, so a stale chart is wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Never becomes renderable, so skip the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely still streaming — fail only if unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // `scheme` comes from the same read as the cache key, so palette and key agree.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n svgCache.set(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by SVG, not `code`: a streaming chart is held, and a remount per chunk\n // would drop zoom/pan.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"flint.js","names":[],"sources":["../src/blocks/FlintChart/flintTheme.ts","../src/blocks/FlintChart/refuseUnsafeSpec.ts","../src/blocks/FlintChart/renderFlintSvg.ts","../src/blocks/FlintChart/FlintChart.tsx"],"sourcesContent":["import type { ChartAssemblyInput } from 'flint-chart';\n\nimport type { ResolvedColorScheme } from '../../components/ThemeProvider/types';\nimport { createVegaColorResolver, SERIES_TOKENS, tierTwoColour } from '../../vega/vegaTokens';\n\n/**\n * Put the design system's `--chart-1..5` palette on a Flint input.\n *\n * - Flint resolves series colours during assembly, so they ride `theme_spec`, not `vegaTheme`.\n * - An author-supplied `theme_spec` is passed through untouched.\n * - A `null` `theme_spec` is replaced like a missing one — flint throws on it.\n */\nexport const withFlintSeriesPalette = (\n input: ChartAssemblyInput,\n scheme: ResolvedColorScheme,\n from?: Element | null,\n): ChartAssemblyInput => {\n if (input.theme_spec !== undefined && input.theme_spec !== null) return input;\n\n const token = createVegaColorResolver(scheme, from);\n const categorical = SERIES_TOKENS.map((name) => token(name));\n\n return {\n ...input,\n theme_spec: {\n ink: {\n // `endpointsAgainstSurface` below needs to know what the chart sits on.\n surface: { canvas: token('card') },\n // Flint's defaults assume a light canvas, so text and structure come from tokens.\n text: {\n primary: token('foreground'),\n secondary: token('muted-foreground'),\n muted: token('muted-foreground'),\n // Ink on a filled (series-coloured) mark.\n inverse: token('card'),\n },\n structure: {\n axis: token('border'),\n grid: token('border'),\n frame: token('border'),\n rule: token('border'),\n },\n series: {\n categorical,\n // Past five series flint swaps `categorical` for its theme-blind `tableau10`;\n // this keeps 6-10 on our hues (`tierTwoColour`).\n categoricalExtended: [\n ...categorical,\n ...categorical.map((colour) => tierTwoColour(colour, scheme)),\n ],\n // The tail past even the extended set.\n overflow: token('muted-foreground'),\n // For a quantitative `color` field; flint's default starts at black, invisible on dark.\n sequential: {\n stops: [token('accent'), token('chart-1')],\n space: 'lab',\n endpointsAgainstSurface: true,\n },\n },\n },\n },\n };\n};\n","/**\n * The boundary in front of the flint renderer: nothing in a spec may fetch or run.\n *\n * - Checked on the input, before `assembleVegaLite`: compiled Vega carries its own `signals`/`expr`.\n * - Refused on a key's presence, not its value: a relative `data.url` is still a fetch.\n */\n\n/** Keys whose presence anywhere in a spec is refused: each fetches, evaluates or binds a signal. */\nconst REFUSED_KEYS = new Set(['url', 'expr', 'signal', 'signals', 'params']);\n\n/** Suffix of Vega-Lite's other expression properties; matched by shape so new ones are refused. */\nconst EXPRESSION_KEY_SUFFIX = /Expr$/;\n\n/**\n * An own property's value, or `undefined`.\n *\n * - Via a descriptor: inherited keys are ignored and a getter is never called.\n */\nconst ownValue = (node: unknown, key: string): unknown => {\n if (typeof node !== 'object' || node === null) return undefined;\n\n return Object.getOwnPropertyDescriptor(node, key)?.value;\n};\n\n/**\n * The chart's rows (`spec.data.values` only), which the walk does not descend into.\n *\n * - Rows are data vega never evaluates, so a column named `url` must not be refused.\n * - Flint takes one top-level dataset, so every other node is spec.\n * - Skipped by identity, not key name: a `values` key elsewhere must not hide a refused key.\n */\nconst inlineRows = (spec: unknown): unknown[] | null => {\n const values = ownValue(ownValue(spec, 'data'), 'values');\n\n return Array.isArray(values) ? values : null;\n};\n\n/**\n * The top-level maps flint keys by column name, so their keys are data, not spec.\n *\n * - Only the keys are exempt; each value is walked like any other node.\n * - Matched by identity, as in `inlineRows`: a nested object with the same name gets no exemption.\n */\nconst COLUMN_KEYED_MAPS = ['semantic_types', 'field_display_names'];\n\nconst columnKeyedMaps = (spec: unknown): unknown[] =>\n COLUMN_KEYED_MAPS.map((key) => ownValue(spec, key)).filter(\n (map) => typeof map === 'object' && map !== null,\n );\n\n/** Ceiling on the nodes the walk visits; exceeding it refuses, since the spec went unchecked. */\nconst MAX_SPEC_NODES = 10_000;\n\nconst TOO_LARGE = 'is too large to check';\n\n/**\n * Why this spec may not be rendered, or `null` if it may.\n *\n * - The reason goes to the console; the reader sees `DiagramSource`'s fallback.\n * - Iterative, so deep nesting cannot overflow the stack; nodes are charged on push.\n * - An accessor is refused, not skipped: its value cannot be read without running it.\n */\nexport const findUnsafeSpecKey = (spec: unknown): string | null => {\n const rows = inlineRows(spec);\n const columnKeyed = columnKeyedMaps(spec);\n const stack: unknown[] = [spec];\n let visited = 1;\n\n while (stack.length > 0) {\n const node = stack.pop();\n\n if (Array.isArray(node)) {\n visited += node.length;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(...node);\n continue;\n }\n\n if (typeof node !== 'object' || node === null) continue;\n\n const keysAreColumnNames = columnKeyed.includes(node);\n\n // Descriptors: skip inherited keys and never call a getter.\n for (const [key, descriptor] of Object.entries(Object.getOwnPropertyDescriptors(node))) {\n if (!keysAreColumnNames && (REFUSED_KEYS.has(key) || EXPRESSION_KEY_SUFFIX.test(key))) {\n return `carries \\`${key}\\``;\n }\n if (!('value' in descriptor)) return `carries an accessor at \\`${key}\\``;\n\n const { value } = descriptor;\n if (rows !== null && value === rows) continue;\n\n visited += 1;\n if (visited > MAX_SPEC_NODES) return TOO_LARGE;\n stack.push(value);\n }\n }\n\n return null;\n};\n","import type { ChartAssemblyInput } from 'flint-chart';\nimport type { assembleVegaLite } from 'flint-chart/vegalite';\nimport type * as vega from 'vega';\nimport type * as vegaLite from 'vega-lite';\nimport type { Config, TopLevelSpec } from 'vega-lite';\n\nimport { findUnsafeSpecKey } from './refuseUnsafeSpec';\n\n// The lazily imported modules the pipeline needs; a parameter so tests can pass a fake runtime.\nexport type FlintRuntime = {\n assembleVegaLite: typeof assembleVegaLite;\n vega: Pick<typeof vega, 'View' | 'parse'>;\n vegaLite: Pick<typeof vegaLite, 'compile'>;\n};\n\n/**\n * Compile a Flint `ChartAssemblyInput` to a static SVG string.\n *\n * - The one place unsafe input is refused (`refuseUnsafeSpec.ts`), before anything compiles it.\n * - Flint's `_`-prefixed hint keys are not Vega-Lite schema and are stripped before compiling.\n * - `config` rides the compile options; flint's spec-side config sets no colours to conflict.\n * - Renders headless; `toSVG()` is the only output.\n */\nexport const renderFlintSvg = async (\n runtime: FlintRuntime,\n input: ChartAssemblyInput,\n config: Config,\n): Promise<string> => {\n const unsafe = findUnsafeSpecKey(input);\n if (unsafe !== null) {\n throw new Error(`Refusing to render a flint spec that ${unsafe}`);\n }\n\n // `assembleVegaLite` returns `any` upstream; without the hint keys it is a Vega-Lite spec.\n const vlSpec: TopLevelSpec & Record<string, unknown> = { ...runtime.assembleVegaLite(input) };\n for (const key of Object.keys(vlSpec)) {\n if (key.startsWith('_')) delete vlSpec[key];\n }\n const { spec } = runtime.vegaLite.compile(vlSpec, { config });\n const view = new runtime.vega.View(runtime.vega.parse(spec), { renderer: 'none' });\n try {\n await view.runAsync();\n return await view.toSVG();\n } finally {\n view.finalize();\n }\n};\n","import { useEffect, useState } from 'react';\nimport type { ReactNode } from 'react';\nimport type { ChartAssemblyInput } from 'flint-chart';\n\nimport { useThemeAttributes } from '../../components/ThemeProvider/useThemeAttributes';\nimport { ZoomableSvg } from '../../components/ZoomableSvg';\nimport { vegaTheme } from '../../vega/vegaTheme';\nimport { DiagramSkeleton } from '../DiagramShared/DiagramSkeleton';\nimport { DiagramSource } from '../DiagramShared/DiagramSource';\nimport { createSvgCache, diagramCacheKey } from '../DiagramShared/svgCache';\nimport { makeResponsive } from '../DiagramShared/svgTransforms';\n\nimport { withFlintSeriesPalette } from './flintTheme';\nimport type { FlintRuntime } from './renderFlintSvg';\nimport { renderFlintSvg } from './renderFlintSvg';\n\nlet runtimePromise: Promise<FlintRuntime> | null = null;\n\n// Memoized chart-stack import, reset on failure so a chunk-load error can retry.\n// Mirrors `getMermaid()` in `MermaidDiagram`.\nconst getFlintRuntime = (): Promise<FlintRuntime> => {\n if (!runtimePromise) {\n runtimePromise = Promise.all([\n import('flint-chart/vegalite'),\n import('vega'),\n import('vega-lite'),\n ])\n .then(([flint, vega, vegaLite]) => ({\n assembleVegaLite: flint.assembleVegaLite,\n vega,\n vegaLite,\n }))\n .catch((err) => {\n runtimePromise = null;\n throw err;\n });\n }\n return runtimePromise;\n};\n\nconst RENDER_DEBOUNCE_MS = 300;\n\n// A streaming fence is unparseable just like a broken one, so unparseable source\n// keeps the skeleton until it has been unchanged this long, then shows as raw source.\nconst PARSE_GRACE_MS = 1500;\n\n// Parse, compile and layout are synchronous, so a larger source would freeze the chat.\n// Far above the prompt's ~50-row inline data cap.\nconst MAX_SOURCE_CHARS = 200_000;\n\n// Values are already `makeResponsive`d, so ZoomableSvg's sanitize memo stays stable.\nconst svgCache = createSvgCache();\n\n// The theme half of a cache key — tells a still-streaming spec from a theme switch.\nconst prevThemeKeyOf = (key: string): string => key.slice(0, key.indexOf('\\n'));\n\ntype Props = {\n /** Raw source of a ```flint fence: a flint-chart `ChartAssemblyInput` as JSON. */\n code: string;\n /** Extra toolbar controls (e.g. `ExportMenu`) for `ZoomableSvg`; receives the sanitized SVG. */\n renderActions?: (safeSvg: string) => ReactNode;\n};\n\nexport const FlintChart = ({ code, renderActions }: Props) => {\n // - Re-renders on a scheme or brand change, same contract as MermaidDiagram.\n // - `scope` is the element tokens are read off (`null` = `<html>`); it is part of `themeKey`.\n const { scheme, cacheKey: themeKey, scope } = useThemeAttributes();\n const cacheKey = diagramCacheKey(code, themeKey);\n\n const [svg, setSvg] = useState<string | null>(() => svgCache.get(cacheKey) ?? null);\n const [renderFailed, setRenderFailed] = useState(false);\n\n // Reset stale state synchronously on a code or theme change (no one-frame flash):\n // 1. A cache hit is applied at once.\n // 2. Same theme, source only grew (still streaming): keep the last chart, no flicker.\n // 3. Anything else clears — colours are baked in, so a stale chart is wrong.\n const [prevKey, setPrevKey] = useState(cacheKey);\n const [prevCode, setPrevCode] = useState(code);\n if (cacheKey !== prevKey) {\n const cached = svgCache.get(cacheKey);\n const sameThemeStillStreaming =\n themeKey === prevThemeKeyOf(prevKey) && code.startsWith(prevCode);\n setPrevKey(cacheKey);\n setPrevCode(code);\n if (cached) setSvg(cached);\n else if (!sameThemeStillStreaming) setSvg(null);\n setRenderFailed(false);\n }\n\n useEffect(() => {\n if (svgCache.has(cacheKey)) return;\n\n let cancelled = false;\n let graceTimer: ReturnType<typeof setTimeout> | undefined;\n\n const debounceTimer = setTimeout(() => {\n if (code.length > MAX_SOURCE_CHARS) {\n // Never becomes renderable, so skip the grace period.\n console.error(\n `[FlintChart] source exceeds ${MAX_SOURCE_CHARS} characters (${code.length}); not rendering`,\n );\n setSvg(null);\n setRenderFailed(true);\n return;\n }\n\n let input: ChartAssemblyInput;\n try {\n input = JSON.parse(code);\n } catch (err) {\n // Likely still streaming — fail only if unchanged when the grace period ends.\n graceTimer = setTimeout(() => {\n console.debug('[FlintChart] source never became parseable JSON:', err);\n setSvg(null);\n setRenderFailed(true);\n }, PARSE_GRACE_MS - RENDER_DEBOUNCE_MS);\n return;\n }\n\n getFlintRuntime()\n .then(async (runtime) => {\n if (cancelled) return;\n // `scheme` comes from the same read as the cache key, so palette and key agree.\n const svgOutput = makeResponsive(\n await renderFlintSvg(\n runtime,\n withFlintSeriesPalette(input, scheme, scope),\n vegaTheme(scheme, scope),\n ),\n );\n if (cancelled) return;\n svgCache.set(cacheKey, svgOutput);\n setSvg(svgOutput);\n setRenderFailed(false);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error('[FlintChart] render failed:', err);\n setSvg(null);\n setRenderFailed(true);\n });\n }, RENDER_DEBOUNCE_MS);\n\n return () => {\n cancelled = true;\n clearTimeout(debounceTimer);\n if (graceTimer) clearTimeout(graceTimer);\n };\n }, [cacheKey, code, scheme, scope]);\n\n if (renderFailed) {\n return <DiagramSource code={code} />;\n }\n\n if (svg) {\n return (\n <ZoomableSvg\n // Keyed by SVG, not `code`: a streaming chart is held, and a remount per chunk\n // would drop zoom/pan.\n key={svg}\n html={svg}\n copyText={`\\`\\`\\`flint\\n${code}\\n\\`\\`\\``}\n className=\"tw:my-3\"\n renderActions={renderActions}\n />\n );\n }\n\n return <DiagramSkeleton />;\n};\n"],"mappings":"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"}
|