basalt-ui 1.30.2 → 1.32.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/AGENTS.md +1 -1
- package/CHANGELOG.md +54 -0
- package/MIGRATING.md +256 -12
- package/agent/rules/basalt-controls.md +19 -19
- package/agent/rules/basalt-mantine.md +5 -5
- package/configs/oxlint-plugin.js +371 -4
- package/configs/oxlint-plugin.test.ts +327 -2
- package/configs/oxlint.json +2 -1
- package/dist/agent-chat/composer.d.ts.map +1 -1
- package/dist/agent-chat/composer.js +41 -43
- package/dist/agent-chat/thread-workspace.d.ts +2 -2
- package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
- package/dist/agent-chat/thread-workspace.js +3 -4
- package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
- package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
- package/dist/agent-chat/use-keyboard-inset.js +34 -0
- package/dist/charts/cursor/touch-pin.d.ts +45 -0
- package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
- package/dist/charts/cursor/touch-pin.js +79 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
- package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
- package/dist/charts/hooks/use-discrete-cursor.js +198 -0
- package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
- package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
- package/dist/charts/hooks/use-hysteresis.js +12 -0
- package/dist/charts/hooks/useBandPlot.d.ts +7 -0
- package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
- package/dist/charts/hooks/useBandPlot.js +62 -18
- package/dist/charts/hooks/useChartCursor.d.ts +40 -11
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
- package/dist/charts/hooks/useChartCursor.js +102 -11
- package/dist/charts/index.d.ts +0 -1
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +0 -10
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +47 -82
- package/dist/charts/kinds/Donut.d.ts +9 -3
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +110 -48
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +138 -145
- package/dist/charts/kinds/Heatmap.d.ts +7 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +37 -16
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +68 -111
- package/dist/charts/kinds/band-chrome.d.ts +31 -0
- package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
- package/dist/charts/kinds/band-chrome.js +71 -0
- package/dist/charts/kinds/donut-layout.d.ts +47 -0
- package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
- package/dist/charts/kinds/donut-layout.js +52 -0
- package/dist/charts/layout/auto-margin.d.ts +7 -2
- package/dist/charts/layout/auto-margin.d.ts.map +1 -1
- package/dist/charts/layout/auto-margin.js +12 -3
- package/dist/charts/layout/end-labels.d.ts +64 -0
- package/dist/charts/layout/end-labels.d.ts.map +1 -0
- package/dist/charts/layout/end-labels.js +76 -0
- package/dist/charts/primitives/Axes.d.ts +32 -4
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +62 -16
- package/dist/charts/primitives/CartesianChart.d.ts +7 -6
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +264 -145
- package/dist/charts/primitives/ChartCard.d.ts +0 -14
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +56 -21
- package/dist/charts/primitives/ChartFrame.d.ts +28 -22
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +116 -34
- package/dist/charts/primitives/ChartLegend.d.ts +19 -15
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +86 -73
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +20 -4
- package/dist/charts/primitives/Crosshair.js +2 -2
- package/dist/charts/primitives/HoverOverlay.d.ts +29 -9
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +54 -27
- package/dist/charts/primitives/HoverOverlay.module.css +13 -0
- package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/dist/charts/primitives/chart-card-context.d.ts +33 -0
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
- package/dist/charts/primitives/chart-card-context.js +18 -0
- package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +75 -34
- package/dist/charts/primitives/chart-layout.d.ts +112 -0
- package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
- package/dist/charts/primitives/chart-layout.js +175 -0
- package/dist/charts/primitives/chart-tier.d.ts +13 -11
- package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
- package/dist/charts/primitives/chart-tier.js +42 -13
- package/dist/charts/primitives/legend-disclosure.d.ts +27 -0
- package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
- package/dist/charts/primitives/legend-disclosure.js +153 -0
- package/dist/charts/series.d.ts +4 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/utils/ticks.d.ts +27 -8
- package/dist/charts/utils/ticks.d.ts.map +1 -1
- package/dist/charts/utils/ticks.js +35 -11
- package/dist/common/errors.d.ts +11 -1
- package/dist/common/errors.d.ts.map +1 -1
- package/dist/common/errors.js +16 -2
- package/dist/common/hit-floor.d.ts +10 -0
- package/dist/common/hit-floor.d.ts.map +1 -0
- package/dist/common/hit-floor.js +6 -0
- package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
- package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
- package/dist/common/use-measured-widths.d.ts +37 -0
- package/dist/common/use-measured-widths.d.ts.map +1 -0
- package/dist/common/use-measured-widths.js +51 -0
- package/dist/common/use-media-query.d.ts +6 -0
- package/dist/common/use-media-query.d.ts.map +1 -0
- package/dist/common/use-media-query.js +40 -0
- package/dist/common/validate.d.ts +9 -0
- package/dist/common/validate.d.ts.map +1 -1
- package/dist/common/validate.js +9 -4
- package/dist/content/article-layout.d.ts +4 -2
- package/dist/content/article-layout.d.ts.map +1 -1
- package/dist/content/article-layout.js +43 -3
- package/dist/content/article-layout.module.css +45 -4
- package/dist/content/article-layout.module.css.d.ts +3 -0
- package/dist/content/code-block.d.ts.map +1 -1
- package/dist/content/code-block.js +40 -2
- package/dist/content/code-block.module.css +32 -2
- package/dist/content/prose.module.css +13 -8
- package/dist/controls/actions.d.ts +29 -6
- package/dist/controls/actions.d.ts.map +1 -1
- package/dist/controls/actions.js +158 -27
- package/dist/controls/actions.module.css +14 -1
- package/dist/controls/actions.module.css.d.ts +1 -0
- package/dist/controls/controls.module.css +13 -31
- package/dist/controls/enum-filter.js +2 -2
- package/dist/controls/filter-context.d.ts +10 -0
- package/dist/controls/filter-context.d.ts.map +1 -1
- package/dist/controls/filter-context.js +2 -0
- package/dist/controls/filter-pill.d.ts.map +1 -1
- package/dist/controls/filter-pill.js +1 -0
- package/dist/controls/index.d.ts +1 -1
- package/dist/controls/index.d.ts.map +1 -1
- package/dist/controls/index.js +0 -2
- package/dist/controls/multi-select-filter.js +2 -2
- package/dist/controls/sync-button.module.css +2 -1
- package/dist/dashboard/section.d.ts.map +1 -1
- package/dist/dashboard/section.js +1 -0
- package/dist/dashboard/section.module.css +1 -26
- package/dist/dashboard/stat-card.d.ts +1 -1
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/dashboard/stat-card.js +4 -1
- package/dist/dashboard/stat-card.module.css +6 -9
- package/dist/dashboard/stat-group.module.css +33 -66
- package/dist/dashboard/widget-grid.d.ts.map +1 -1
- package/dist/dashboard/widget-grid.module.css +11 -38
- package/dist/dashboard/widget-header.d.ts +14 -4
- package/dist/dashboard/widget-header.d.ts.map +1 -1
- package/dist/dashboard/widget-header.js +23 -6
- package/dist/dashboard/widget-header.module.css +145 -133
- package/dist/dashboard/widget-header.module.css.d.ts +2 -0
- package/dist/data/data-table-state.d.ts +83 -0
- package/dist/data/data-table-state.d.ts.map +1 -0
- package/dist/data/data-table-state.js +139 -0
- package/dist/data/data-table.d.ts +56 -2
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +436 -336
- package/dist/data/data-table.module.css +94 -0
- package/dist/data/data-table.module.css.d.ts +11 -0
- package/dist/data/virtual-list.d.ts.map +1 -1
- package/dist/data/virtual-list.js +10 -0
- package/dist/forms/form-layout.d.ts.map +1 -1
- package/dist/forms/form-layout.js +3 -3
- package/dist/forms/form-layout.module.css +20 -5
- package/dist/forms/form-layout.module.css.d.ts +1 -0
- package/dist/guard/index.d.ts +8 -2
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +68 -2
- package/dist/guard/types.d.ts +3 -3
- package/dist/guard/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/provider/index.d.ts +20 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +30 -8
- package/dist/shell/app-brand.d.ts.map +1 -1
- package/dist/shell/app-brand.js +1 -0
- package/dist/shell/app-brand.module.css +2 -2
- package/dist/shell/app-header.module.css +18 -8
- package/dist/shell/app-main.module.css +54 -3
- package/dist/shell/app-main.module.css.d.ts +1 -0
- package/dist/shell/app-mobile-nav.module.css +16 -4
- package/dist/shell/app-sidebar.module.css +4 -6
- package/dist/shell/aside-docking.d.ts +31 -0
- package/dist/shell/aside-docking.d.ts.map +1 -0
- package/dist/shell/aside-docking.js +19 -0
- package/dist/shell/index.d.ts +1 -0
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +56 -16
- package/dist/shell/mobile-nav-model.d.ts +1 -1
- package/dist/shell/page-aside.d.ts +22 -3
- package/dist/shell/page-aside.d.ts.map +1 -1
- package/dist/shell/page-aside.js +27 -13
- package/dist/shell/page-bar.d.ts.map +1 -1
- package/dist/shell/page-bar.js +10 -5
- package/dist/shell/page-bar.module.css +10 -34
- package/dist/shell/use-breakpoint.d.ts +3 -4
- package/dist/shell/use-breakpoint.d.ts.map +1 -1
- package/dist/shell/use-breakpoint.js +3 -25
- package/dist/shell/use-size-class.d.ts +6 -0
- package/dist/shell/use-size-class.d.ts.map +1 -0
- package/dist/shell/use-size-class.js +19 -0
- package/dist/state/persisted-or-local.d.ts +4 -1
- package/dist/state/persisted-or-local.d.ts.map +1 -1
- package/dist/state/persisted-or-local.js +4 -3
- package/dist/state/persisted.d.ts +10 -0
- package/dist/state/persisted.d.ts.map +1 -1
- package/dist/state/persisted.js +44 -14
- package/dist/styles.css +41 -0
- package/dist/surfaces.d.ts +4 -4
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +11 -3
- package/dist/theme/controls.module.css +7 -0
- package/dist/theme/ctl-theme.d.ts +1 -0
- package/dist/theme/ctl-theme.d.ts.map +1 -1
- package/dist/theme/ctl-theme.js +12 -4
- package/dist/theme/floating.module.css +37 -0
- package/dist/theme/floating.module.css.d.ts +3 -0
- package/dist/theme/index.d.ts +2 -2
- package/dist/theme/index.d.ts.map +1 -1
- package/dist/theme/index.js +34 -5
- package/dist/theme/nav-link.module.css +7 -0
- package/dist/theme-lab/derive-controls.d.ts.map +1 -1
- package/dist/tokens/index.d.ts +7 -16
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +23 -28
- package/dist/tokens/palette.d.ts +5 -27
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +8 -30
- package/dist/tokens/size-classes.d.ts +40 -0
- package/dist/tokens/size-classes.d.ts.map +1 -0
- package/dist/tokens/size-classes.js +29 -0
- package/dist/tokens.css +8 -4
- package/llms.txt +3 -3
- package/package.json +18 -17
- package/src/agent-chat/composer.tsx +74 -47
- package/src/agent-chat/thread-workspace.tsx +8 -11
- package/src/agent-chat/use-keyboard-inset.ts +68 -0
- package/src/charts/cursor/touch-pin.ts +138 -0
- package/src/charts/hooks/use-discrete-cursor.ts +335 -0
- package/src/charts/hooks/use-hysteresis.ts +26 -0
- package/src/charts/hooks/useBandPlot.ts +90 -27
- package/src/charts/hooks/useChartCursor.ts +211 -26
- package/src/charts/index.ts +0 -8
- package/src/charts/kinds/BandStrip.tsx +25 -61
- package/src/charts/kinds/Donut.tsx +153 -62
- package/src/charts/kinds/DualPanel.tsx +21 -26
- package/src/charts/kinds/Heatmap.tsx +61 -22
- package/src/charts/kinds/MirroredBars.tsx +23 -65
- package/src/charts/kinds/band-chrome.tsx +91 -0
- package/src/charts/kinds/donut-layout.ts +82 -0
- package/src/charts/layout/auto-margin.ts +24 -5
- package/src/charts/layout/end-labels.ts +135 -0
- package/src/charts/primitives/Axes.tsx +112 -21
- package/src/charts/primitives/CartesianChart.tsx +282 -111
- package/src/charts/primitives/ChartCard.tsx +119 -48
- package/src/charts/primitives/ChartFrame.tsx +204 -60
- package/src/charts/primitives/ChartLegend.tsx +115 -78
- package/src/charts/primitives/ChartTooltip.tsx +24 -3
- package/src/charts/primitives/Crosshair.tsx +2 -2
- package/src/charts/primitives/HoverOverlay.module.css +13 -0
- package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
- package/src/charts/primitives/HoverOverlay.tsx +62 -20
- package/src/charts/primitives/chart-card-context.ts +46 -0
- package/src/charts/primitives/chart-frame-layout.ts +207 -182
- package/src/charts/primitives/chart-layout.ts +318 -0
- package/src/charts/primitives/chart-tier.tsx +66 -21
- package/src/charts/primitives/legend-disclosure.tsx +177 -0
- package/src/charts/series.ts +4 -1
- package/src/charts/utils/ticks.ts +95 -19
- package/src/common/errors.ts +25 -1
- package/src/common/hit-floor.ts +9 -0
- package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
- package/src/common/use-measured-widths.ts +88 -0
- package/src/common/use-media-query.ts +56 -0
- package/src/common/validate.ts +17 -4
- package/src/content/article-layout.module.css +45 -4
- package/src/content/article-layout.module.css.d.ts +3 -0
- package/src/content/article-layout.tsx +60 -5
- package/src/content/code-block.module.css +32 -2
- package/src/content/code-block.tsx +47 -3
- package/src/content/prose.module.css +13 -8
- package/src/controls/actions.module.css +14 -1
- package/src/controls/actions.module.css.d.ts +1 -0
- package/src/controls/actions.tsx +223 -75
- package/src/controls/controls.module.css +13 -31
- package/src/controls/enum-filter.tsx +2 -2
- package/src/controls/filter-context.tsx +11 -0
- package/src/controls/filter-pill.tsx +1 -0
- package/src/controls/index.ts +0 -1
- package/src/controls/multi-select-filter.tsx +2 -2
- package/src/controls/sync-button.module.css +2 -1
- package/src/dashboard/section.module.css +1 -26
- package/src/dashboard/section.tsx +1 -0
- package/src/dashboard/stat-card.module.css +6 -9
- package/src/dashboard/stat-card.tsx +6 -3
- package/src/dashboard/stat-group.module.css +33 -66
- package/src/dashboard/widget-grid.module.css +11 -38
- package/src/dashboard/widget-grid.tsx +7 -6
- package/src/dashboard/widget-header.module.css +145 -133
- package/src/dashboard/widget-header.module.css.d.ts +2 -0
- package/src/dashboard/widget-header.tsx +45 -11
- package/src/data/data-table-state.ts +218 -0
- package/src/data/data-table.module.css +94 -0
- package/src/data/data-table.module.css.d.ts +11 -0
- package/src/data/data-table.tsx +547 -299
- package/src/data/virtual-list.tsx +31 -0
- package/src/forms/form-layout.module.css +20 -5
- package/src/forms/form-layout.module.css.d.ts +1 -0
- package/src/forms/form-layout.tsx +12 -10
- package/src/guard/index.ts +188 -3
- package/src/guard/types.ts +3 -2
- package/src/index.ts +2 -0
- package/src/notifications/center.tsx +3 -0
- package/src/provider/index.tsx +60 -7
- package/src/shell/app-brand.module.css +2 -2
- package/src/shell/app-brand.tsx +1 -0
- package/src/shell/app-header.module.css +18 -8
- package/src/shell/app-main.module.css +54 -3
- package/src/shell/app-main.module.css.d.ts +1 -0
- package/src/shell/app-mobile-nav.module.css +16 -4
- package/src/shell/app-sidebar.module.css +4 -6
- package/src/shell/aside-docking.ts +54 -0
- package/src/shell/index.tsx +178 -129
- package/src/shell/mobile-nav-model.ts +1 -1
- package/src/shell/page-aside.tsx +70 -21
- package/src/shell/page-bar.module.css +10 -34
- package/src/shell/page-bar.tsx +10 -5
- package/src/shell/use-breakpoint.ts +9 -45
- package/src/shell/use-size-class.ts +31 -0
- package/src/state/persisted-or-local.ts +10 -4
- package/src/state/persisted.ts +76 -17
- package/src/styles.css +41 -0
- package/src/surfaces.ts +9 -1
- package/src/theme/controls.module.css +7 -0
- package/src/theme/ctl-theme.tsx +15 -3
- package/src/theme/floating.module.css +37 -0
- package/src/theme/floating.module.css.d.ts +3 -0
- package/src/theme/index.ts +46 -8
- package/src/theme/nav-link.module.css +7 -0
- package/src/theme-lab/derive-controls.tsx +2 -0
- package/src/tokens/index.ts +20 -30
- package/src/tokens/palette.ts +6 -28
- package/src/tokens/size-classes.ts +46 -0
- package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
- /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
|
@@ -19,15 +19,18 @@ import basaltPlugin, {
|
|
|
19
19
|
KNOWN_RULE_IDS,
|
|
20
20
|
PLUGIN_RULE_ADVISORY,
|
|
21
21
|
PLUGIN_RULE_GRACE,
|
|
22
|
+
RAW_BREAKPOINT_RECIPE,
|
|
22
23
|
RETIRED_RULE_IDS,
|
|
23
24
|
} from './oxlint-plugin.js'
|
|
24
25
|
import { CTL_THEME } from '../src/theme/ctl-theme'
|
|
25
26
|
import {
|
|
27
|
+
GRACE_PERIOD_KINDS,
|
|
26
28
|
GUARD_RULES,
|
|
27
29
|
PLUGIN_RULE_IDS,
|
|
28
30
|
WAIVER_ID_ALIASES as GUARD_WAIVER_ID_ALIASES,
|
|
29
31
|
} from '../src/guard/index.ts'
|
|
30
32
|
import { PLUGIN_RULE_ID_LIST, SURFACES } from '../src/surfaces.ts'
|
|
33
|
+
import { CONTAINER_CLASSES } from '../src/tokens/size-classes.ts'
|
|
31
34
|
|
|
32
35
|
const PLUGIN_PATH = resolve(import.meta.dirname, 'oxlint-plugin.js')
|
|
33
36
|
const OXLINT_BIN = resolve(import.meta.dirname, '..', '..', '..', 'node_modules', '.bin', 'oxlint')
|
|
@@ -70,6 +73,7 @@ beforeEach(() => {
|
|
|
70
73
|
'basalt/agent-resume-guard': 'error',
|
|
71
74
|
'basalt/agent-no-raw-usechat': 'error',
|
|
72
75
|
'basalt/ai-sdk-major': 'error',
|
|
76
|
+
'basalt/raw-breakpoint': 'error',
|
|
73
77
|
},
|
|
74
78
|
}),
|
|
75
79
|
)
|
|
@@ -1331,6 +1335,42 @@ describe('assertGraceLedger', () => {
|
|
|
1331
1335
|
})
|
|
1332
1336
|
})
|
|
1333
1337
|
|
|
1338
|
+
// ── twin lanes: one law, two lanes, one promotion ─────────────────────────────
|
|
1339
|
+
// A guard kind and a plugin rule that enforce ONE law in two lanes (CSS/text vs. JSX AST) must
|
|
1340
|
+
// promote together — a consumer failing one spelling and warned on the other is the confusion the
|
|
1341
|
+
// pairing exists to prevent. `raw-media-query` split from `basalt/raw-breakpoint` exactly that way
|
|
1342
|
+
// (1.32.0 vs 1.33.0). Twins are DERIVED, not hand-listed, from the two places they are declared: a
|
|
1343
|
+
// `WAIVER_ID_ALIASES` pair, and a guard entry whose `why` calls itself the twin of `basalt/<id>`.
|
|
1344
|
+
|
|
1345
|
+
const pluginGrace = PLUGIN_RULE_GRACE as Record<string, GraceEntry>
|
|
1346
|
+
const guardGrace = GRACE_PERIOD_KINDS as Record<string, GraceEntry>
|
|
1347
|
+
|
|
1348
|
+
function twinLanes(): Array<[kind: string, rule: string]> {
|
|
1349
|
+
const pairs = new Map<string, string>()
|
|
1350
|
+
for (const [kind, aliases] of Object.entries(WAIVER_ID_ALIASES)) {
|
|
1351
|
+
for (const rule of aliases) if (kind in guardGrace || rule in pluginGrace) pairs.set(kind, rule)
|
|
1352
|
+
}
|
|
1353
|
+
for (const [kind, entry] of Object.entries(guardGrace)) {
|
|
1354
|
+
const match = /twin of the oxlint plugin's `basalt\/([a-z-]+)`/.exec(entry.why)
|
|
1355
|
+
if (match?.[1] !== undefined) pairs.set(kind, match[1])
|
|
1356
|
+
}
|
|
1357
|
+
return [...pairs].filter(([kind]) => kind in guardGrace)
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
describe('twin lanes share one promotion', () => {
|
|
1361
|
+
it('derives the raw-media-query ↔ raw-breakpoint pair (the derivation is not silently empty)', () => {
|
|
1362
|
+
expect(twinLanes()).toContainEqual(['raw-media-query', 'raw-breakpoint'])
|
|
1363
|
+
})
|
|
1364
|
+
|
|
1365
|
+
it.each(twinLanes())('%s and basalt/%s are both in grace with the same promote', (kind, rule) => {
|
|
1366
|
+
expect([kind, rule, guardGrace[kind]?.promote]).toEqual([
|
|
1367
|
+
kind,
|
|
1368
|
+
rule,
|
|
1369
|
+
pluginGrace[rule]?.promote,
|
|
1370
|
+
])
|
|
1371
|
+
})
|
|
1372
|
+
})
|
|
1373
|
+
|
|
1334
1374
|
describe('PLUGIN_RULE_GRACE', () => {
|
|
1335
1375
|
const shipped = JSON.parse(readFileSync(resolve(import.meta.dirname, 'oxlint.json'), 'utf8')) as {
|
|
1336
1376
|
rules: Record<string, unknown>
|
|
@@ -2735,10 +2775,11 @@ describe('basalt/responsive-twin', () => {
|
|
|
2735
2775
|
})
|
|
2736
2776
|
|
|
2737
2777
|
it('does NOT flag two halves on DIFFERENT breakpoints', () => {
|
|
2738
|
-
|
|
2778
|
+
// `code` is not asserted here: raw visibleFrom/hiddenFrom outside a shell home is its own,
|
|
2779
|
+
// unrelated finding (basalt/raw-breakpoint) — this test is only about responsive-twin's silence.
|
|
2780
|
+
const { rules } = run(
|
|
2739
2781
|
`export const C = () => (\n <div>\n <SegmentedControl visibleFrom="sm" data={[]} />\n <SegmentedControl hiddenFrom="md" data={[]} />\n </div>\n)\n`,
|
|
2740
2782
|
)
|
|
2741
|
-
expect(code).toBe(0)
|
|
2742
2783
|
expect(rules).not.toContain('responsive-twin')
|
|
2743
2784
|
})
|
|
2744
2785
|
|
|
@@ -3400,6 +3441,290 @@ describe('basalt/forms-field-key', () => {
|
|
|
3400
3441
|
})
|
|
3401
3442
|
})
|
|
3402
3443
|
|
|
3444
|
+
// ── raw-breakpoint ────────────────────────────────────────────────────────────
|
|
3445
|
+
|
|
3446
|
+
describe('basalt/raw-breakpoint', () => {
|
|
3447
|
+
it('flags a responsive-object prop on an imported Mantine component', () => {
|
|
3448
|
+
const { rules } = run(
|
|
3449
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3450
|
+
`export const C = () => <SimpleGrid cols={{ base: 1, sm: 2 }} />\n`,
|
|
3451
|
+
)
|
|
3452
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3453
|
+
})
|
|
3454
|
+
|
|
3455
|
+
it('flags a responsive object nested one level down (Mantine AppShell idiom)', () => {
|
|
3456
|
+
const { rules } = run(
|
|
3457
|
+
`import { AppShell } from '@mantine/core'\n` +
|
|
3458
|
+
`export const C = () => <AppShell footer={{ height: { base: 60, sm: 0 } }}>hi</AppShell>\n`,
|
|
3459
|
+
)
|
|
3460
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3461
|
+
})
|
|
3462
|
+
|
|
3463
|
+
it('flags a responsive object behind a same-file const binding', () => {
|
|
3464
|
+
const { rules } = run(
|
|
3465
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3466
|
+
`const responsiveCols = { base: 1, sm: 2 }\n` +
|
|
3467
|
+
`export const C = () => <SimpleGrid cols={responsiveCols} />\n`,
|
|
3468
|
+
)
|
|
3469
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3470
|
+
})
|
|
3471
|
+
|
|
3472
|
+
// The nested-property half: a property VALUE that is itself an Identifier must resolve through
|
|
3473
|
+
// the same-file const map, exactly as the attribute's own top-level value already does.
|
|
3474
|
+
it('flags a responsive object behind a const used as a nested property value', () => {
|
|
3475
|
+
const { rules } = run(
|
|
3476
|
+
`import { AppShell } from '@mantine/core'\n` +
|
|
3477
|
+
`const h = { base: 60, sm: 0 }\n` +
|
|
3478
|
+
`export const C = () => <AppShell footer={{ height: h }}>hi</AppShell>\n`,
|
|
3479
|
+
)
|
|
3480
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3481
|
+
})
|
|
3482
|
+
|
|
3483
|
+
// A name declared twice in the file has no single binding without a scope manager, so it is
|
|
3484
|
+
// dropped rather than resolved last-declaration-wins — the outer (non-responsive) `cols` the JSX
|
|
3485
|
+
// actually references must not be flagged via the inner function's responsive one.
|
|
3486
|
+
it('does NOT flag a name redeclared in the file — an ambiguous binding is skipped, not guessed', () => {
|
|
3487
|
+
const { rules } = run(
|
|
3488
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3489
|
+
`const cols = { fixed: 2 }\n` +
|
|
3490
|
+
`function Other() {\n const cols = { base: 1, sm: 2 }\n return null\n}\n` +
|
|
3491
|
+
`export const C = () => <SimpleGrid cols={cols} />\n`,
|
|
3492
|
+
)
|
|
3493
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3494
|
+
})
|
|
3495
|
+
|
|
3496
|
+
it('does NOT flag a fixed cols on the same component', () => {
|
|
3497
|
+
const { rules } = run(
|
|
3498
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3499
|
+
`export const C = () => <SimpleGrid cols={2} />\n`,
|
|
3500
|
+
)
|
|
3501
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3502
|
+
})
|
|
3503
|
+
|
|
3504
|
+
it('does NOT flag an object prop on a component never imported from @mantine/*', () => {
|
|
3505
|
+
const { rules } = run(`export const C = () => <WidgetGrid cols={{ base: 1, sm: 2 }} />\n`)
|
|
3506
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3507
|
+
})
|
|
3508
|
+
|
|
3509
|
+
// The recipe's px keys are CONTAINER_CLASSES written out by hand in two places — the plugin
|
|
3510
|
+
// (plain JS, cannot import TS source) and MIGRATING's rule entry. Retuning a class must fail here.
|
|
3511
|
+
describe('recipe px literals ↔ CONTAINER_CLASSES', () => {
|
|
3512
|
+
const boundaries = new Set<number>(Object.values(CONTAINER_CLASSES).filter((px) => px > 0))
|
|
3513
|
+
const pxLiterals = (text: string) => [...text.matchAll(/(\d+)px/g)].map((m) => Number(m[1]))
|
|
3514
|
+
|
|
3515
|
+
it('every px key in the rule messages is a CONTAINER_CLASSES boundary', () => {
|
|
3516
|
+
const found = pxLiterals(RAW_BREAKPOINT_RECIPE as string)
|
|
3517
|
+
expect(found.length).toBeGreaterThan(0)
|
|
3518
|
+
for (const px of found) expect([px, boundaries.has(px)]).toEqual([px, true])
|
|
3519
|
+
})
|
|
3520
|
+
|
|
3521
|
+
it("every px value and the class table in MIGRATING's raw-breakpoint entry match", () => {
|
|
3522
|
+
const migrating = readFileSync(resolve(import.meta.dir, '../MIGRATING.md'), 'utf8')
|
|
3523
|
+
const start = migrating.indexOf('### `basalt/raw-breakpoint`')
|
|
3524
|
+
expect(start).toBeGreaterThan(-1)
|
|
3525
|
+
const end = migrating.indexOf('\n### ', start + 1)
|
|
3526
|
+
const section = migrating.slice(start, end === -1 ? undefined : end)
|
|
3527
|
+
for (const px of pxLiterals(section)) expect([px, boundaries.has(px)]).toEqual([px, true])
|
|
3528
|
+
const { compact, regular, wide } = CONTAINER_CLASSES
|
|
3529
|
+
expect(section).toContain(`compact ${compact} · regular ${regular} · wide ${wide}`)
|
|
3530
|
+
})
|
|
3531
|
+
})
|
|
3532
|
+
|
|
3533
|
+
// The sanctioned answer, read from @mantine/core 9.3 source: SimpleGrid type="container" writes
|
|
3534
|
+
// each key verbatim as `@container simple-grid (min-width: <key>)`, Grid type="container" only
|
|
3535
|
+
// goes container WITH a `breakpoints` map. Exempt exactly where Mantine resolves the object.
|
|
3536
|
+
describe('Mantine type="container" grids', () => {
|
|
3537
|
+
const grid = (jsx: string) =>
|
|
3538
|
+
run(`import { Grid, SimpleGrid } from '@mantine/core'\nexport const C = () => ${jsx}\n`)
|
|
3539
|
+
const CONTAINER_MESSAGE = /Theme breakpoint name under type="container"/
|
|
3540
|
+
|
|
3541
|
+
it('does NOT flag a px-keyed container SimpleGrid (the recipe)', () => {
|
|
3542
|
+
expect(
|
|
3543
|
+
grid(`<SimpleGrid type="container" cols={{ base: 1, '480px': 2, '800px': 3 }} />`).rules,
|
|
3544
|
+
).not.toContain('raw-breakpoint')
|
|
3545
|
+
})
|
|
3546
|
+
|
|
3547
|
+
it('does NOT flag a lone `base` under type="container"', () => {
|
|
3548
|
+
expect(grid(`<SimpleGrid type="container" cols={{ base: 2 }} />`).rules).not.toContain(
|
|
3549
|
+
'raw-breakpoint',
|
|
3550
|
+
)
|
|
3551
|
+
})
|
|
3552
|
+
|
|
3553
|
+
it('flags a theme-named key on a container SimpleGrid — Mantine emits a dead (min-width: sm)', () => {
|
|
3554
|
+
const { rules, output } = grid(`<SimpleGrid type="container" cols={{ base: 1, sm: 2 }} />`)
|
|
3555
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3556
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(true)
|
|
3557
|
+
})
|
|
3558
|
+
|
|
3559
|
+
it('still flags type="media" (and a dynamic type) with the viewport message', () => {
|
|
3560
|
+
for (const type of [`"media"`, `{kind}`]) {
|
|
3561
|
+
const { rules, output } = grid(`<SimpleGrid type=${type} cols={{ base: 1, sm: 2 }} />`)
|
|
3562
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3563
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(false)
|
|
3564
|
+
}
|
|
3565
|
+
})
|
|
3566
|
+
|
|
3567
|
+
it('does NOT flag Grid.Col spans under a container Grid with a breakpoints map', () => {
|
|
3568
|
+
const src =
|
|
3569
|
+
`<Grid type="container" breakpoints={{ xs: '240px', sm: '480px' }}>\n` +
|
|
3570
|
+
` {items.map((i) => <Grid.Col key={i} span={{ base: 12, sm: 6 }}>x</Grid.Col>)}\n` +
|
|
3571
|
+
`</Grid>`
|
|
3572
|
+
expect(grid(src).rules).not.toContain('raw-breakpoint')
|
|
3573
|
+
})
|
|
3574
|
+
|
|
3575
|
+
it('flags Grid.Col spans under a container Grid with NO breakpoints — Mantine falls back to media', () => {
|
|
3576
|
+
const { rules, output } = grid(
|
|
3577
|
+
`<Grid type="container"><Grid.Col span={{ base: 12, sm: 6 }}>x</Grid.Col></Grid>`,
|
|
3578
|
+
)
|
|
3579
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3580
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(true)
|
|
3581
|
+
})
|
|
3582
|
+
|
|
3583
|
+
it('reports a const-extracted theme-keyed object on a container SimpleGrid — still a dead query', () => {
|
|
3584
|
+
const { rules, output } = run(
|
|
3585
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3586
|
+
`const cols = { base: 1, sm: 2 }\n` +
|
|
3587
|
+
`export const C = () => <SimpleGrid type="container" cols={cols} />\n`,
|
|
3588
|
+
)
|
|
3589
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3590
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(true)
|
|
3591
|
+
})
|
|
3592
|
+
|
|
3593
|
+
// Grid goes container only on a truthy map, and an empty one emits no query — so the exemption
|
|
3594
|
+
// needs a map that statically resolves to a non-empty object, not the attribute's presence.
|
|
3595
|
+
it.each([
|
|
3596
|
+
['undefined', `breakpoints={undefined}`, ''],
|
|
3597
|
+
['an empty object', `breakpoints={{}}`, ''],
|
|
3598
|
+
['an unresolvable identifier', `breakpoints={imported}`, `import { imported } from './bp'\n`],
|
|
3599
|
+
])('flags Grid.Col spans under a container Grid whose breakpoints is %s', (_, bp, extra) => {
|
|
3600
|
+
const { rules, output } = run(
|
|
3601
|
+
`import { Grid } from '@mantine/core'\n${extra}` +
|
|
3602
|
+
`export const C = () => <Grid type="container" ${bp}><Grid.Col span={{ base: 12, sm: 6 }}>x</Grid.Col></Grid>\n`,
|
|
3603
|
+
)
|
|
3604
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3605
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(true)
|
|
3606
|
+
})
|
|
3607
|
+
|
|
3608
|
+
it('does NOT flag Grid.Col spans under a container Grid whose breakpoints is a same-file const', () => {
|
|
3609
|
+
const { rules } = run(
|
|
3610
|
+
`import { Grid } from '@mantine/core'\n` +
|
|
3611
|
+
`const bp = { xs: '240px', sm: '480px', md: '800px', lg: '800px', xl: '800px' }\n` +
|
|
3612
|
+
`export const C = () => <Grid type="container" breakpoints={bp}><Grid.Col span={{ base: 12, sm: 6 }}>x</Grid.Col></Grid>\n`,
|
|
3613
|
+
)
|
|
3614
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3615
|
+
})
|
|
3616
|
+
|
|
3617
|
+
// Only the grid-keyed props route through the container queries; Box style props on the same
|
|
3618
|
+
// element still compile to viewport @media, so they keep the ordinary viewport message.
|
|
3619
|
+
it.each([
|
|
3620
|
+
[
|
|
3621
|
+
'SimpleGrid p',
|
|
3622
|
+
`<SimpleGrid type="container" cols={{ base: 1, '480px': 2 }} p={{ base: 'xs', lg: 'md' }} />`,
|
|
3623
|
+
],
|
|
3624
|
+
[
|
|
3625
|
+
'Grid mt',
|
|
3626
|
+
`<Grid type="container" breakpoints={{ xs: '240px', sm: '480px', md: '800px', lg: '800px', xl: '800px' }} mt={{ base: 0, sm: 'md' }}>x</Grid>`,
|
|
3627
|
+
],
|
|
3628
|
+
[
|
|
3629
|
+
'Grid.Col mih',
|
|
3630
|
+
`<Grid type="container" breakpoints={{ xs: '240px', sm: '480px', md: '800px', lg: '800px', xl: '800px' }}><Grid.Col span={6} mih={{ base: 80, lg: 120 }}>x</Grid.Col></Grid>`,
|
|
3631
|
+
],
|
|
3632
|
+
])('flags a Box style prop on a container grid as viewport (%s)', (_, jsx) => {
|
|
3633
|
+
const { rules, output } = grid(jsx)
|
|
3634
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3635
|
+
expect(CONTAINER_MESSAGE.test(output)).toBe(false)
|
|
3636
|
+
})
|
|
3637
|
+
|
|
3638
|
+
it('does NOT flag container-routed spacing props on a container grid', () => {
|
|
3639
|
+
const src =
|
|
3640
|
+
`<Grid type="container" breakpoints={{ xs: '240px', sm: '480px', md: '800px', lg: '800px', xl: '800px' }} gap={{ base: 'xs', sm: 'md' }}>` +
|
|
3641
|
+
`<Grid.Col span={{ base: 12, sm: 6 }} order={{ base: 2, sm: 1 }}>x</Grid.Col></Grid>`
|
|
3642
|
+
expect(grid(src).rules).not.toContain('raw-breakpoint')
|
|
3643
|
+
})
|
|
3644
|
+
|
|
3645
|
+
it('still flags Grid.Col spans under an ordinary Grid', () => {
|
|
3646
|
+
expect(
|
|
3647
|
+
grid(`<Grid><Grid.Col span={{ base: 12, sm: 6 }}>x</Grid.Col></Grid>`).rules,
|
|
3648
|
+
).toContain('raw-breakpoint')
|
|
3649
|
+
})
|
|
3650
|
+
})
|
|
3651
|
+
|
|
3652
|
+
it('flags visibleFrom/hiddenFrom outside a shell home', () => {
|
|
3653
|
+
const { rules } = run(
|
|
3654
|
+
`import { Box } from '@mantine/core'\n` +
|
|
3655
|
+
`export const C = () => <Box visibleFrom="sm">desktop only</Box>\n`,
|
|
3656
|
+
)
|
|
3657
|
+
expect(rules).toContain('raw-breakpoint')
|
|
3658
|
+
})
|
|
3659
|
+
|
|
3660
|
+
it('does NOT flag visibleFrom/hiddenFrom inside a file that DEFINES BasaltShell', () => {
|
|
3661
|
+
const { rules } = run(
|
|
3662
|
+
`import { Box } from '@mantine/core'\n` +
|
|
3663
|
+
`export function BasaltShell() {\n return <Box visibleFrom="sm">desktop only</Box>\n}\n`,
|
|
3664
|
+
)
|
|
3665
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3666
|
+
})
|
|
3667
|
+
|
|
3668
|
+
// The exemption is uniform across all four shapes: a file that IS the shell may also carry a
|
|
3669
|
+
// responsive-object prop, not only visibleFrom/hiddenFrom.
|
|
3670
|
+
it('does NOT flag a responsive-object prop inside a file that DEFINES BasaltShell', () => {
|
|
3671
|
+
const { rules } = run(
|
|
3672
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3673
|
+
`export function BasaltShell() {\n return null\n}\n` +
|
|
3674
|
+
`export const C = () => <SimpleGrid cols={{ base: 1, sm: 2 }} />\n`,
|
|
3675
|
+
)
|
|
3676
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3677
|
+
})
|
|
3678
|
+
|
|
3679
|
+
it('does NOT flag visibleFrom/hiddenFrom inside a file that DEFINES a C9 control (ViewTabs) and imports no basalt-ui', () => {
|
|
3680
|
+
const { rules } = run(
|
|
3681
|
+
`import { Box } from '@mantine/core'\n` +
|
|
3682
|
+
`export function ViewTabs() {\n return <Box visibleFrom="sm">desktop only</Box>\n}\n`,
|
|
3683
|
+
)
|
|
3684
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3685
|
+
})
|
|
3686
|
+
|
|
3687
|
+
it('flags an import of useMediaQuery/useMatches/useViewportSize from @mantine/*', () => {
|
|
3688
|
+
expect(run(`import { useMediaQuery } from '@mantine/hooks'\n`).rules).toContain(
|
|
3689
|
+
'raw-breakpoint',
|
|
3690
|
+
)
|
|
3691
|
+
expect(run(`import { useMatches } from '@mantine/core'\n`).rules).toContain('raw-breakpoint')
|
|
3692
|
+
expect(run(`import { useViewportSize } from '@mantine/hooks'\n`).rules).toContain(
|
|
3693
|
+
'raw-breakpoint',
|
|
3694
|
+
)
|
|
3695
|
+
})
|
|
3696
|
+
|
|
3697
|
+
it('does NOT flag useMatches imported from a non-Mantine router (name collision)', () => {
|
|
3698
|
+
// @tanstack/react-router (and react-router) export their own unrelated `useMatches` —
|
|
3699
|
+
// basalt's own `src/router-tanstack/index.ts` imports it; scoped to `@mantine/*` sources only.
|
|
3700
|
+
expect(run(`import { useMatches } from '@tanstack/react-router'\n`).rules).not.toContain(
|
|
3701
|
+
'raw-breakpoint',
|
|
3702
|
+
)
|
|
3703
|
+
expect(run(`import { useViewportSize } from 'some-lib'\n`).rules).not.toContain(
|
|
3704
|
+
'raw-breakpoint',
|
|
3705
|
+
)
|
|
3706
|
+
})
|
|
3707
|
+
|
|
3708
|
+
it('flags window.matchMedia and window.innerWidth', () => {
|
|
3709
|
+
expect(run(`const mq = window.matchMedia('(max-width: 600px)')\n`).rules).toContain(
|
|
3710
|
+
'raw-breakpoint',
|
|
3711
|
+
)
|
|
3712
|
+
expect(run(`const w = window.innerWidth\n`).rules).toContain('raw-breakpoint')
|
|
3713
|
+
})
|
|
3714
|
+
|
|
3715
|
+
it('flags a bare matchMedia() call', () => {
|
|
3716
|
+
expect(run(`const mq = matchMedia('(max-width: 600px)')\n`).rules).toContain('raw-breakpoint')
|
|
3717
|
+
})
|
|
3718
|
+
|
|
3719
|
+
it('honors a scoped theme-allow on the responsive-object prop', () => {
|
|
3720
|
+
const { rules } = run(
|
|
3721
|
+
`import { SimpleGrid } from '@mantine/core'\n` +
|
|
3722
|
+
`export const C = () => (\n // theme-allow raw-breakpoint — migrating in a follow-up\n <SimpleGrid cols={{ base: 1, sm: 2 }} />\n)\n`,
|
|
3723
|
+
)
|
|
3724
|
+
expect(rules).not.toContain('raw-breakpoint')
|
|
3725
|
+
})
|
|
3726
|
+
})
|
|
3727
|
+
|
|
3403
3728
|
// ── the deprecation ledger (B4) ──────────────────────────────────────────────
|
|
3404
3729
|
|
|
3405
3730
|
describe('DEPRECATED_EXPORTS', () => {
|
package/configs/oxlint.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/agent-chat/composer.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,cAAc,EAAkB,GAAG,EAAiB,SAAS,EAAE,GAAG,EAAE,MAAM,OAAO,CAAA;AAG/F,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"composer.d.ts","sourceRoot":"","sources":["../../src/agent-chat/composer.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { ActionIcon, Group, Kbd, Stack, Text, Textarea } from "@mantine/core";
|
|
2
|
+
import { ActionIcon, Box, Group, Kbd, Stack, Text, Textarea } from "@mantine/core";
|
|
3
3
|
import { useImperativeHandle, useLayoutEffect, useRef, useSyncExternalStore } from "react";
|
|
4
4
|
import { cx } from "../common/props.js";
|
|
5
5
|
import { readPersistedValue } from "../state.js";
|
|
6
6
|
import { VX } from "../tokens/index.js";
|
|
7
|
+
import { useKeyboardInsetRef } from "./use-keyboard-inset.js";
|
|
7
8
|
function SendGlyph() {
|
|
8
9
|
return /* @__PURE__ */ jsx("svg", { width: 16, height: 16, viewBox: "0 0 24 24", fill: "none", "aria-hidden": true, children: /* @__PURE__ */ jsx(
|
|
9
10
|
"path",
|
|
@@ -90,6 +91,7 @@ const noopSubscribe = (_cb) => () => {
|
|
|
90
91
|
};
|
|
91
92
|
const emptyDraft = () => "";
|
|
92
93
|
const NO_ATTACHMENTS = [];
|
|
94
|
+
const ROOT_GAP = 6;
|
|
93
95
|
function liveSelection(el) {
|
|
94
96
|
if (document.activeElement !== el) return null;
|
|
95
97
|
const { selectionStart, selectionEnd } = el;
|
|
@@ -170,6 +172,7 @@ function Composer({
|
|
|
170
172
|
textareaRef.current?.focus();
|
|
171
173
|
};
|
|
172
174
|
useImperativeHandle(ref, () => ({ insertText, setValue, focus: focusTextarea }));
|
|
175
|
+
const keyboardInsetRef = useKeyboardInsetRef();
|
|
173
176
|
const submit = () => {
|
|
174
177
|
if (inputDisabled || !hasPayload) return;
|
|
175
178
|
const submittedText = trimmed;
|
|
@@ -221,48 +224,43 @@ function Composer({
|
|
|
221
224
|
if (onPaste !== void 0) textareaProps.onPaste = onPaste;
|
|
222
225
|
if (classNames?.input !== void 0) textareaProps.className = cx(classNames.input);
|
|
223
226
|
const showActions = showStop || showSend;
|
|
224
|
-
return /* @__PURE__ */ jsxs(
|
|
225
|
-
Stack,
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
] }),
|
|
262
|
-
hint === void 0 ? /* @__PURE__ */ jsx(DefaultHint, {}) : hint
|
|
263
|
-
]
|
|
264
|
-
}
|
|
265
|
-
);
|
|
227
|
+
return /* @__PURE__ */ jsxs(Box, { ref: keyboardInsetRef, className: cx(classNames?.root, className), style, children: [
|
|
228
|
+
/* @__PURE__ */ jsxs(Stack, { gap: ROOT_GAP, children: [
|
|
229
|
+
/* @__PURE__ */ jsxs(Group, { gap: "xs", align: "flex-end", wrap: "nowrap", children: [
|
|
230
|
+
leftSection,
|
|
231
|
+
/* @__PURE__ */ jsx(Textarea, { ...textareaProps, ref: textareaRef }),
|
|
232
|
+
rightSection,
|
|
233
|
+
showActions && /* @__PURE__ */ jsxs(Group, { gap: "xs", wrap: "nowrap", className: cx(classNames?.actions), children: [
|
|
234
|
+
showStop ? /* @__PURE__ */ jsx(
|
|
235
|
+
ActionIcon,
|
|
236
|
+
{
|
|
237
|
+
size: 42,
|
|
238
|
+
radius: "md",
|
|
239
|
+
variant: "filled",
|
|
240
|
+
color: "red",
|
|
241
|
+
onClick: onStop,
|
|
242
|
+
"aria-label": "Stop generating",
|
|
243
|
+
children: /* @__PURE__ */ jsx(StopGlyph, {})
|
|
244
|
+
}
|
|
245
|
+
) : null,
|
|
246
|
+
showSend ? /* @__PURE__ */ jsx(
|
|
247
|
+
ActionIcon,
|
|
248
|
+
{
|
|
249
|
+
size: 42,
|
|
250
|
+
radius: "md",
|
|
251
|
+
variant: "filled",
|
|
252
|
+
onClick: submit,
|
|
253
|
+
disabled: inputDisabled || !hasPayload,
|
|
254
|
+
"aria-label": "Send message",
|
|
255
|
+
children: /* @__PURE__ */ jsx(SendGlyph, {})
|
|
256
|
+
}
|
|
257
|
+
) : null
|
|
258
|
+
] })
|
|
259
|
+
] }),
|
|
260
|
+
hint === void 0 ? /* @__PURE__ */ jsx(DefaultHint, {}) : hint
|
|
261
|
+
] }),
|
|
262
|
+
/* @__PURE__ */ jsx(Box, { "aria-hidden": true, style: { height: "var(--vx-keyboard-inset, 0px)" } })
|
|
263
|
+
] });
|
|
266
264
|
}
|
|
267
265
|
export {
|
|
268
266
|
Composer
|
|
@@ -3,7 +3,7 @@ import type { AgentPart, AgentTransport, OutcomeResolver, ThreadsStore } from '.
|
|
|
3
3
|
import type { BasaltProps, SlotStylesProps } from '../common/props.js';
|
|
4
4
|
/** The four boxes `ThreadWorkspace` paints: `root` is the outermost pane split, `feed` is the
|
|
5
5
|
* thread-list pane, `composer` is the new-thread composer pinned under the feed, `detail` is the
|
|
6
|
-
* right-hand (or,
|
|
6
|
+
* right-hand (or, in the `compact` size class, full-screen) open-thread pane. */
|
|
7
7
|
export type ThreadWorkspaceSlot = 'root' | 'feed' | 'composer' | 'detail';
|
|
8
8
|
export type ThreadWorkspaceProps = BasaltProps & SlotStylesProps<ThreadWorkspaceSlot> & {
|
|
9
9
|
/**
|
|
@@ -35,7 +35,7 @@ export type ThreadWorkspaceProps = BasaltProps & SlotStylesProps<ThreadWorkspace
|
|
|
35
35
|
/**
|
|
36
36
|
* A two-pane master-detail thread workspace: a scrollable feed of threads with an anchored
|
|
37
37
|
* new-thread composer, and a detail panel for the open thread's live transcript. Collapses to a
|
|
38
|
-
* single pane
|
|
38
|
+
* single pane in the `compact` size class (< 840px).
|
|
39
39
|
*
|
|
40
40
|
* @example
|
|
41
41
|
* <ThreadWorkspace useThreads={useThreads} transport={transport} resolveOutcome={heuristicOutcome} />
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"thread-workspace.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-workspace.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"thread-workspace.d.ts","sourceRoot":"","sources":["../../src/agent-chat/thread-workspace.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAGxF,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAcnE;;iFAEiF;AACjF,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,GAAG,UAAU,GAAG,QAAQ,CAAA;AAEzE,MAAM,MAAM,oBAAoB,GAAG,WAAW,GAC5C,eAAe,CAAC,mBAAmB,CAAC,GAAG;IACrC;;;OAGG;IACH,QAAQ,CAAC,UAAU,EAAE,MAAM,YAAY,CAAA;IACvC,mEAAmE;IACnE,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAC,SAAS,EAAE,MAAM,CAAC,CAAA;IACrD,mEAAmE;IACnE,QAAQ,CAAC,cAAc,EAAE,eAAe,CAAC,SAAS,CAAC,CAAA;IACnD,qEAAqE;IACrE,QAAQ,CAAC,oBAAoB,CAAC,EAAE,MAAM,CAAA;IACtC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAA;CAChC,CAAA;AA4DH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,EAC9B,UAAU,EACV,SAAS,EACT,cAAc,EACd,oBAAoB,EACpB,UAAU,EACV,UAAU,EACV,SAAS,EACT,KAAK,EACL,UAAU,GACX,EAAE,oBAAoB,GAAG,GAAG,CAAC,OAAO,CAmHpC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Box, Divider, Flex, Skeleton, Stack, Text
|
|
3
|
-
import { useMediaQuery } from "@mantine/hooks";
|
|
2
|
+
import { Box, Divider, Flex, Skeleton, Stack, Text } from "@mantine/core";
|
|
4
3
|
import { useAgentThreadRuns } from "../agent/index.js";
|
|
5
4
|
import { cx } from "../common/props.js";
|
|
6
5
|
import { assertRequiredProps } from "../common/validate.js";
|
|
6
|
+
import { useSizeClass } from "../shell/use-size-class.js";
|
|
7
7
|
import { VX } from "../tokens/index.js";
|
|
8
8
|
import { Composer } from "./composer.js";
|
|
9
9
|
import { ThreadDetailPanel } from "./thread-detail-panel.js";
|
|
@@ -54,8 +54,7 @@ function ThreadWorkspace({
|
|
|
54
54
|
assertRequiredProps("ThreadWorkspace", { useThreads }, ["useThreads"]);
|
|
55
55
|
const store = useThreads();
|
|
56
56
|
const runs = useAgentThreadRuns({ transport, store, resolveOutcome });
|
|
57
|
-
const
|
|
58
|
-
const narrow = useMediaQuery(`(max-width: calc(${theme.breakpoints.sm} - 0.00625em))`);
|
|
57
|
+
const narrow = useSizeClass() === "compact";
|
|
59
58
|
const threads = store.threads;
|
|
60
59
|
const activeThread = threads.find((thread) => thread.id === store.activeId) ?? null;
|
|
61
60
|
const activeRun = store.activeId !== null ? runs.runs.get(store.activeId) : void 0;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-keyboard-inset.d.ts","sourceRoot":"","sources":["../../src/agent-chat/use-keyboard-inset.ts"],"names":[],"mappings":"AAiCA,wBAAgB,mBAAmB,IAAI,CAAC,EAAE,EAAE,WAAW,GAAG,IAAI,KAAK,IAAI,CAkCtE"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { useCallback, useState } from "react";
|
|
2
|
+
import { useIsomorphicLayoutEffect } from "../common/isomorphic-layout-effect.js";
|
|
3
|
+
const KEYBOARD_INSET_VAR = "--vx-keyboard-inset";
|
|
4
|
+
function useKeyboardInsetRef() {
|
|
5
|
+
const [node, setNode] = useState(null);
|
|
6
|
+
const attach = useCallback((el) => {
|
|
7
|
+
setNode(el);
|
|
8
|
+
}, []);
|
|
9
|
+
useIsomorphicLayoutEffect(() => {
|
|
10
|
+
if (node === null) return;
|
|
11
|
+
if (typeof window === "undefined") return;
|
|
12
|
+
const viewport = window.visualViewport;
|
|
13
|
+
if (viewport === null || viewport === void 0) return;
|
|
14
|
+
const publish = () => {
|
|
15
|
+
const inset = Math.max(
|
|
16
|
+
0,
|
|
17
|
+
document.documentElement.clientHeight - viewport.height - viewport.offsetTop
|
|
18
|
+
);
|
|
19
|
+
node.style.setProperty(KEYBOARD_INSET_VAR, `${inset}px`);
|
|
20
|
+
};
|
|
21
|
+
publish();
|
|
22
|
+
viewport.addEventListener("resize", publish);
|
|
23
|
+
viewport.addEventListener("scroll", publish);
|
|
24
|
+
return () => {
|
|
25
|
+
viewport.removeEventListener("resize", publish);
|
|
26
|
+
viewport.removeEventListener("scroll", publish);
|
|
27
|
+
node.style.removeProperty(KEYBOARD_INSET_VAR);
|
|
28
|
+
};
|
|
29
|
+
}, [node]);
|
|
30
|
+
return attach;
|
|
31
|
+
}
|
|
32
|
+
export {
|
|
33
|
+
useKeyboardInsetRef
|
|
34
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Outside-tap / Escape / scroll (optionally resize) dismissal for a pinned/open floating element.
|
|
3
|
+
* Capture-phase `pointerdown` so a descendant that stops propagation on its own handler can't leave
|
|
4
|
+
* the dismissal stuck.
|
|
5
|
+
*/
|
|
6
|
+
export declare function useDismissOnOutside({ enabled, isInside, onDismiss, dismissOnResize, scrollExemptInside, }: {
|
|
7
|
+
enabled: boolean;
|
|
8
|
+
/** A tap (or, with `scrollExemptInside`, a scroll) landing where this returns true doesn't
|
|
9
|
+
* dismiss. Omit to treat every tap as outside. */
|
|
10
|
+
isInside?: (target: Node) => boolean;
|
|
11
|
+
onDismiss: () => void;
|
|
12
|
+
/** Also dismiss on a window `resize` — `ChartLegend`'s disclosure needs this, the touch-pinned
|
|
13
|
+
* cursors don't (their host doesn't reflow independently of the chart it's pinned to). */
|
|
14
|
+
dismissOnResize?: boolean;
|
|
15
|
+
/** Exempts a scroll whose target is `isInside` — `ChartLegend`'s own scrollable panel needs this
|
|
16
|
+
* (`overflowY: auto`), the cursor hooks don't (a chart has no internal scroller to protect). */
|
|
17
|
+
scrollExemptInside?: boolean;
|
|
18
|
+
}): void;
|
|
19
|
+
export type TouchPin = {
|
|
20
|
+
/** `onPointerDown` for a coarse press: snapshots whatever is currently committed (via
|
|
21
|
+
* `getCommitted`) so a later `cancel` can restore exactly that, then tracks `pointerId`. */
|
|
22
|
+
begin: (pointerId: number) => void;
|
|
23
|
+
/** `onPointerUp` for the tracked pointer: the press is now committed — nothing left to revert. */
|
|
24
|
+
commit: (pointerId: number) => boolean;
|
|
25
|
+
/** `onPointerCancel`/`onPointerLeave` for the tracked pointer: the browser took the gesture (a
|
|
26
|
+
* scroll) before the press committed — restores the pre-press value via `setCommitted`, or
|
|
27
|
+
* leaves it clear when nothing was committed before. Returns whether this pointer was tracked. */
|
|
28
|
+
cancel: (pointerId: number) => boolean;
|
|
29
|
+
/** Hard reset — no restore, just drop any in-flight press bookkeeping (an explicit `clear()`, e.g.
|
|
30
|
+
* Escape or an outside tap, is about to overwrite the value directly anyway). */
|
|
31
|
+
reset: () => void;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Provisional-press state machine shared by `useChartCursor`/`useDiscreteCursor`: a coarse press
|
|
35
|
+
* shows a value immediately (PROVISIONAL, not yet committed) — `commit` on `pointerup` leaves it in
|
|
36
|
+
* place, `cancel` on `pointercancel`/`pointerleave` (a scroll winning the gesture) restores whatever
|
|
37
|
+
* was committed before this press started, rather than dropping it. This is what keeps a previously
|
|
38
|
+
* COMMITTED pin alive through a second tap that gets cancelled instead of completed.
|
|
39
|
+
*/
|
|
40
|
+
export declare function useTouchPin<V>({ getCommitted, setCommitted, }: {
|
|
41
|
+
/** Reads the value currently committed — snapshotted on `begin`, restored on `cancel`. */
|
|
42
|
+
getCommitted: () => V;
|
|
43
|
+
setCommitted: (value: V) => void;
|
|
44
|
+
}): TouchPin;
|
|
45
|
+
//# sourceMappingURL=touch-pin.d.ts.map
|