termcast 1.3.47 → 1.3.49
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/apis/cache.d.ts.map +1 -1
- package/dist/apis/cache.js +1 -2
- package/dist/apis/cache.js.map +1 -1
- package/dist/apis/localstorage.d.ts.map +1 -1
- package/dist/apis/localstorage.js +1 -2
- package/dist/apis/localstorage.js.map +1 -1
- package/dist/apis/sqlite.d.ts +7 -0
- package/dist/apis/sqlite.d.ts.map +1 -0
- package/dist/apis/sqlite.js +13 -0
- package/dist/apis/sqlite.js.map +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +14 -5
- package/dist/build.js.map +1 -1
- package/dist/cli.js +5 -40
- package/dist/cli.js.map +1 -1
- package/dist/colors.d.ts +7 -7
- package/dist/colors.js +7 -7
- package/dist/compile.d.ts +6 -1
- package/dist/compile.d.ts.map +1 -1
- package/dist/compile.js +46 -27
- package/dist/compile.js.map +1 -1
- package/dist/components/actions.js +1 -1
- package/dist/components/actions.js.map +1 -1
- package/dist/components/bar-chart.d.ts +38 -0
- package/dist/components/bar-chart.d.ts.map +1 -0
- package/dist/components/bar-chart.js +158 -0
- package/dist/components/bar-chart.js.map +1 -0
- package/dist/components/bar-graph.d.ts +41 -0
- package/dist/components/bar-graph.d.ts.map +1 -0
- package/dist/components/bar-graph.js +95 -0
- package/dist/components/bar-graph.js.map +1 -0
- package/dist/components/detail.d.ts.map +1 -1
- package/dist/components/detail.js +5 -7
- package/dist/components/detail.js.map +1 -1
- package/dist/components/footer.d.ts.map +1 -1
- package/dist/components/footer.js +8 -9
- package/dist/components/footer.js.map +1 -1
- package/dist/components/form/date-picker.d.ts.map +1 -1
- package/dist/components/form/date-picker.js +7 -1
- package/dist/components/form/date-picker.js.map +1 -1
- package/dist/components/form/dropdown.d.ts.map +1 -1
- package/dist/components/form/dropdown.js +10 -2
- package/dist/components/form/dropdown.js.map +1 -1
- package/dist/components/form/index.d.ts.map +1 -1
- package/dist/components/form/index.js +4 -5
- package/dist/components/form/index.js.map +1 -1
- package/dist/components/form/use-form-navigation.d.ts.map +1 -1
- package/dist/components/form/use-form-navigation.js +6 -0
- package/dist/components/form/use-form-navigation.js.map +1 -1
- package/dist/components/graph.d.ts +111 -0
- package/dist/components/graph.d.ts.map +1 -0
- package/dist/components/graph.js +392 -0
- package/dist/components/graph.js.map +1 -0
- package/dist/components/icon.js +5 -5
- package/dist/components/icon.js.map +1 -1
- package/dist/components/list.d.ts +53 -5
- package/dist/components/list.d.ts.map +1 -1
- package/dist/components/list.js +125 -71
- package/dist/components/list.js.map +1 -1
- package/dist/components/loading-bar.js +3 -3
- package/dist/components/loading-bar.js.map +1 -1
- package/dist/components/loading-text.d.ts +1 -1
- package/dist/components/loading-text.d.ts.map +1 -1
- package/dist/components/loading-text.js +3 -1
- package/dist/components/loading-text.js.map +1 -1
- package/dist/components/metadata.js +2 -2
- package/dist/components/metadata.js.map +1 -1
- package/dist/components/row.d.ts +10 -0
- package/dist/components/row.d.ts.map +1 -0
- package/dist/components/row.js +12 -0
- package/dist/components/row.js.map +1 -0
- package/dist/components/table.d.ts +57 -0
- package/dist/components/table.d.ts.map +1 -0
- package/dist/components/table.js +365 -0
- package/dist/components/table.js.map +1 -0
- package/dist/descendants.js +13 -13
- package/dist/descendants.js.map +1 -1
- package/dist/examples/bar-graph-weekly.d.ts +2 -0
- package/dist/examples/bar-graph-weekly.d.ts.map +1 -0
- package/dist/examples/bar-graph-weekly.js +95 -0
- package/dist/examples/bar-graph-weekly.js.map +1 -0
- package/dist/examples/components-weird-places.d.ts +2 -0
- package/dist/examples/components-weird-places.d.ts.map +1 -0
- package/dist/examples/components-weird-places.js +46 -0
- package/dist/examples/components-weird-places.js.map +1 -0
- package/dist/examples/graph-bar-chart.d.ts +2 -0
- package/dist/examples/graph-bar-chart.d.ts.map +1 -0
- package/dist/examples/graph-bar-chart.js +270 -0
- package/dist/examples/graph-bar-chart.js.map +1 -0
- package/dist/examples/graph-multi-series.d.ts +2 -0
- package/dist/examples/graph-multi-series.d.ts.map +1 -0
- package/dist/examples/graph-multi-series.js +23 -0
- package/dist/examples/graph-multi-series.js.map +1 -0
- package/dist/examples/graph-polymarket.d.ts +2 -0
- package/dist/examples/graph-polymarket.d.ts.map +1 -0
- package/dist/examples/graph-polymarket.js +109 -0
- package/dist/examples/graph-polymarket.js.map +1 -0
- package/dist/examples/graph-row.d.ts +2 -0
- package/dist/examples/graph-row.d.ts.map +1 -0
- package/dist/examples/graph-row.js +226 -0
- package/dist/examples/graph-row.js.map +1 -0
- package/dist/examples/graph-styles.d.ts +2 -0
- package/dist/examples/graph-styles.d.ts.map +1 -0
- package/dist/examples/graph-styles.js +316 -0
- package/dist/examples/graph-styles.js.map +1 -0
- package/dist/examples/list-accessory-table.d.ts +2 -0
- package/dist/examples/list-accessory-table.d.ts.map +1 -0
- package/dist/examples/list-accessory-table.js +46 -0
- package/dist/examples/list-accessory-table.js.map +1 -0
- package/dist/examples/list-item-accessories.d.ts +2 -0
- package/dist/examples/list-item-accessories.d.ts.map +1 -0
- package/dist/examples/list-item-accessories.js +27 -0
- package/dist/examples/list-item-accessories.js.map +1 -0
- package/dist/examples/list-no-actions.d.ts +2 -0
- package/dist/examples/list-no-actions.d.ts.map +1 -0
- package/dist/examples/list-no-actions.js +7 -0
- package/dist/examples/list-no-actions.js.map +1 -0
- package/dist/examples/simple-detail-table.d.ts +2 -0
- package/dist/examples/simple-detail-table.d.ts.map +1 -0
- package/dist/examples/simple-detail-table.js +45 -0
- package/dist/examples/simple-detail-table.js.map +1 -0
- package/dist/examples/simple-graph.d.ts +2 -0
- package/dist/examples/simple-graph.d.ts.map +1 -0
- package/dist/examples/simple-graph.js +32 -0
- package/dist/examples/simple-graph.js.map +1 -0
- package/dist/examples/simple-table-wrap.d.ts +2 -0
- package/dist/examples/simple-table-wrap.d.ts.map +1 -0
- package/dist/examples/simple-table-wrap.js +37 -0
- package/dist/examples/simple-table-wrap.js.map +1 -0
- package/dist/examples/table-edge-cases.d.ts +2 -0
- package/dist/examples/table-edge-cases.d.ts.map +1 -0
- package/dist/examples/table-edge-cases.js +70 -0
- package/dist/examples/table-edge-cases.js.map +1 -0
- package/dist/examples/table-flex-grow.d.ts +2 -0
- package/dist/examples/table-flex-grow.d.ts.map +1 -0
- package/dist/examples/table-flex-grow.js +18 -0
- package/dist/examples/table-flex-grow.js.map +1 -0
- package/dist/extensions/dev.d.ts.map +1 -1
- package/dist/extensions/dev.js +5 -1
- package/dist/extensions/dev.js.map +1 -1
- package/dist/globals.d.ts +1 -0
- package/dist/globals.d.ts.map +1 -1
- package/dist/globals.js +2 -0
- package/dist/globals.js.map +1 -1
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/date-picker-widget.d.ts.map +1 -1
- package/dist/internal/date-picker-widget.js +4 -0
- package/dist/internal/date-picker-widget.js.map +1 -1
- package/dist/internal/providers.d.ts.map +1 -1
- package/dist/internal/providers.js +1 -3
- package/dist/internal/providers.js.map +1 -1
- package/dist/logger.d.ts.map +1 -1
- package/dist/logger.js +2 -1
- package/dist/logger.js.map +1 -1
- package/dist/markdown-utils.d.ts +22 -1
- package/dist/markdown-utils.d.ts.map +1 -1
- package/dist/markdown-utils.js +66 -1
- package/dist/markdown-utils.js.map +1 -1
- package/dist/opentui.d.ts +4 -0
- package/dist/opentui.d.ts.map +1 -0
- package/dist/opentui.js +3 -0
- package/dist/opentui.js.map +1 -0
- package/dist/release.d.ts +2 -1
- package/dist/release.d.ts.map +1 -1
- package/dist/release.js +2 -1
- package/dist/release.js.map +1 -1
- package/dist/state.d.ts +1 -0
- package/dist/state.d.ts.map +1 -1
- package/dist/state.js +1 -1
- package/dist/state.js.map +1 -1
- package/dist/swift-runtime.d.ts.map +1 -1
- package/dist/swift-runtime.js +20 -5
- package/dist/swift-runtime.js.map +1 -1
- package/dist/theme.d.ts +1 -0
- package/dist/theme.d.ts.map +1 -1
- package/dist/theme.js +13 -0
- package/dist/theme.js.map +1 -1
- package/dist/themes/nerv.json +227 -0
- package/dist/themes/termcast.json +72 -71
- package/dist/themes.d.ts +2 -1
- package/dist/themes.d.ts.map +1 -1
- package/dist/themes.js +7 -5
- package/dist/themes.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +4 -1
- package/dist/utils.js.map +1 -1
- package/package.json +12 -4
- package/src/apis/cache.test.ts +1 -1
- package/src/apis/cache.tsx +1 -2
- package/src/apis/localstorage.tsx +1 -2
- package/src/apis/sqlite.ts +14 -0
- package/src/build.tsx +15 -5
- package/src/cli.tsx +5 -49
- package/src/colors.tsx +7 -7
- package/src/compile.tsx +53 -30
- package/src/components/actions.tsx +1 -1
- package/src/components/bar-chart.tsx +271 -0
- package/src/components/bar-graph.tsx +214 -0
- package/src/components/detail.tsx +7 -8
- package/src/components/footer.tsx +14 -15
- package/src/components/form/date-picker.tsx +9 -0
- package/src/components/form/dropdown.tsx +13 -3
- package/src/components/form/index.tsx +4 -6
- package/src/components/form/use-form-navigation.tsx +6 -0
- package/src/components/graph.tsx +506 -0
- package/src/components/icon.tsx +5 -5
- package/src/components/list.tsx +210 -102
- package/src/components/loading-bar.tsx +3 -3
- package/src/components/loading-text.tsx +4 -2
- package/src/components/metadata.tsx +2 -2
- package/src/components/row.tsx +31 -0
- package/src/components/table.tsx +511 -0
- package/src/descendants.tsx +13 -13
- package/src/examples/action-shortcut.vitest.tsx +1 -1
- package/src/examples/actions-context.vitest.tsx +1 -1
- package/src/examples/bar-graph-weekly.tsx +264 -0
- package/src/examples/bar-graph-weekly.vitest.tsx +275 -0
- package/src/examples/detail-metadata-showcase.vitest.tsx +22 -22
- package/src/examples/form-basic.vitest.tsx +239 -0
- package/src/examples/form-dropdown.vitest.tsx +29 -29
- package/src/examples/form-tagpicker.vitest.tsx +27 -27
- package/src/examples/github.vitest.tsx +4 -4
- package/src/examples/graph-bar-chart.tsx +408 -0
- package/src/examples/graph-bar-chart.vitest.tsx +283 -0
- package/src/examples/graph-multi-series.tsx +36 -0
- package/src/examples/graph-multi-series.vitest.tsx +89 -0
- package/src/examples/graph-polymarket.tsx +182 -0
- package/src/examples/graph-polymarket.vitest.tsx +130 -0
- package/src/examples/graph-row.tsx +347 -0
- package/src/examples/graph-row.vitest.tsx +295 -0
- package/src/examples/graph-styles.tsx +457 -0
- package/src/examples/graph-styles.vitest.tsx +322 -0
- package/src/examples/list-accessory-table.tsx +77 -0
- package/src/examples/list-detail-metadata.vitest.tsx +21 -21
- package/src/examples/list-dropdown-default.vitest.tsx +12 -12
- package/src/examples/list-item-accessories.tsx +106 -0
- package/src/examples/list-item-accessories.vitest.tsx +115 -0
- package/src/examples/list-no-actions.tsx +18 -0
- package/src/examples/list-no-actions.vitest.tsx +97 -0
- package/src/examples/list-spacing-mode.vitest.tsx +6 -6
- package/src/examples/list-with-detail.vitest.tsx +73 -73
- package/src/examples/list-with-dropdown.vitest.tsx +49 -6
- package/src/examples/list-with-sections.vitest.tsx +61 -56
- package/src/examples/simple-detail-markdown.vitest.tsx +21 -18
- package/src/examples/simple-detail-table.tsx +65 -0
- package/src/examples/simple-detail-table.vitest.tsx +200 -0
- package/src/examples/simple-graph.tsx +51 -0
- package/src/examples/simple-graph.vitest.tsx +124 -0
- package/src/examples/simple-grid.vitest.tsx +3 -3
- package/src/examples/simple-list-search.vitest.tsx +65 -0
- package/src/examples/simple-navigation.vitest.tsx +3 -3
- package/src/examples/simple-table-wrap.tsx +55 -0
- package/src/examples/simple-table-wrap.vitest.tsx +91 -0
- package/src/examples/store.vitest.tsx +1 -1
- package/src/examples/table-edge-cases.tsx +72 -0
- package/src/examples/table-edge-cases.vitest.tsx +307 -0
- package/src/examples/table-flex-grow.tsx +53 -0
- package/src/examples/table-flex-grow.vitest.tsx +124 -0
- package/src/extensions/dev.tsx +7 -1
- package/src/globals.ts +3 -0
- package/src/index.tsx +31 -0
- package/src/internal/date-picker-widget.tsx +4 -0
- package/src/internal/providers.tsx +1 -4
- package/src/logger.tsx +2 -1
- package/src/markdown-utils.tsx +82 -1
- package/src/opentui.tsx +5 -0
- package/src/release.tsx +3 -0
- package/src/state.tsx +2 -1
- package/src/swift-runtime.tsx +19 -5
- package/src/theme.tsx +14 -0
- package/src/themes/nerv.json +231 -0
- package/src/themes/termcast.json +75 -71
- package/src/themes.ts +8 -5
- package/src/utils.test.tsx +1 -1
- package/src/utils.tsx +5 -1
|
@@ -198,6 +198,9 @@ const DropdownContent = ({
|
|
|
198
198
|
const isInFocus = useIsInFocus()
|
|
199
199
|
const focusContext = useFocusContext()
|
|
200
200
|
const { focusedField, setFocusedField } = focusContext
|
|
201
|
+
const { navigateToPrevious, navigateToNext } = useFormNavigationHelpers(
|
|
202
|
+
props.id,
|
|
203
|
+
)
|
|
201
204
|
const isFocused = focusedField === props.id
|
|
202
205
|
const [focusedIndex, setFocusedIndex] = useState(0)
|
|
203
206
|
|
|
@@ -295,7 +298,6 @@ const DropdownContent = ({
|
|
|
295
298
|
// Handle keyboard navigation when focused
|
|
296
299
|
useKeyboard((evt) => {
|
|
297
300
|
if (!isFocused || !isInFocus) return
|
|
298
|
-
|
|
299
301
|
const items = Object.values(descendantsContext.committedMap)
|
|
300
302
|
.filter((item) => item.index !== -1)
|
|
301
303
|
.sort((a, b) => a.index - b.index)
|
|
@@ -303,7 +305,11 @@ const DropdownContent = ({
|
|
|
303
305
|
|
|
304
306
|
if (itemCount > 0) {
|
|
305
307
|
if (evt.name === 'down') {
|
|
306
|
-
if (focusedIndex >= itemCount - 1)
|
|
308
|
+
if (focusedIndex >= itemCount - 1) {
|
|
309
|
+
navigateToNext()
|
|
310
|
+
evt.stopPropagation()
|
|
311
|
+
return
|
|
312
|
+
}
|
|
307
313
|
const nextIndex = focusedIndex + 1
|
|
308
314
|
const nextItem = items[nextIndex]
|
|
309
315
|
if (nextItem) {
|
|
@@ -313,7 +319,11 @@ const DropdownContent = ({
|
|
|
313
319
|
scrollToItemIfNeeded({ item: nextItem, direction: 1 })
|
|
314
320
|
}
|
|
315
321
|
} else if (evt.name === 'up') {
|
|
316
|
-
if (focusedIndex <= 0)
|
|
322
|
+
if (focusedIndex <= 0) {
|
|
323
|
+
navigateToPrevious()
|
|
324
|
+
evt.stopPropagation()
|
|
325
|
+
return
|
|
326
|
+
}
|
|
317
327
|
const nextIndex = focusedIndex - 1
|
|
318
328
|
const nextItem = items[nextIndex]
|
|
319
329
|
if (nextItem) {
|
|
@@ -331,10 +331,8 @@ export const Form: FormType = ((props) => {
|
|
|
331
331
|
}
|
|
332
332
|
|
|
333
333
|
if (evt.name === 'k' && evt.ctrl) {
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
useStore.setState({ showActionsDialog: true })
|
|
337
|
-
}
|
|
334
|
+
// Always open — built-in actions (Change Theme, etc.) are always available
|
|
335
|
+
useStore.setState({ showActionsDialog: true })
|
|
338
336
|
} else if ((evt.name === 'return' && evt.ctrl) || (evt.name === 'return' && evt.meta)) {
|
|
339
337
|
// Ctrl+Return or Cmd+Return auto-executes first action via ActionPanel
|
|
340
338
|
if (props.actions) {
|
|
@@ -413,8 +411,8 @@ export const Form: FormType = ((props) => {
|
|
|
413
411
|
</box>
|
|
414
412
|
</ScrollBox>
|
|
415
413
|
<FormFooter />
|
|
416
|
-
{/*
|
|
417
|
-
{props.actions
|
|
414
|
+
{/* Always mount ActionPanel offscreen so built-in actions are available */}
|
|
415
|
+
<Offscreen>{props.actions || <ActionPanel />}</Offscreen>
|
|
418
416
|
</box>
|
|
419
417
|
</box>
|
|
420
418
|
</FocusContext.Provider>
|
|
@@ -68,11 +68,17 @@ export function useFormNavigation(
|
|
|
68
68
|
} else {
|
|
69
69
|
navigateToNext()
|
|
70
70
|
}
|
|
71
|
+
evt.stopPropagation()
|
|
71
72
|
} else if (handleArrows) {
|
|
73
|
+
// Prevent the newly-focused field from also processing this arrow.
|
|
74
|
+
// setFocusedField uses flushSync which updates all useKeyboard handler
|
|
75
|
+
// refs via useEffectEvent before the next handler in the dispatch loop runs.
|
|
72
76
|
if (evt.name === 'up') {
|
|
73
77
|
navigateToPrevious()
|
|
78
|
+
evt.stopPropagation()
|
|
74
79
|
} else if (evt.name === 'down') {
|
|
75
80
|
navigateToNext()
|
|
81
|
+
evt.stopPropagation()
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
84
|
})
|
|
@@ -0,0 +1,506 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Graph component for rendering line charts in the terminal using braille characters.
|
|
3
|
+
*
|
|
4
|
+
* Uses a custom opentui Renderable (GraphPlotRenderable) for the actual plot drawing,
|
|
5
|
+
* registered via extend() so it can be used as <graph-plot> in JSX. The plot renderable
|
|
6
|
+
* draws directly to OptimizedBuffer using setCell() with braille Unicode characters
|
|
7
|
+
* (U+2800-U+28FF), giving 2x horizontal and 4x vertical sub-pixel resolution per
|
|
8
|
+
* terminal character cell.
|
|
9
|
+
*
|
|
10
|
+
* The React <Graph> component is a thin wrapper that collects series data from
|
|
11
|
+
* <Graph.Line> children, computes axis ranges, and passes everything to the renderable.
|
|
12
|
+
*
|
|
13
|
+
* Braille dot layout per cell:
|
|
14
|
+
* col0 col1
|
|
15
|
+
* 1 8 row0
|
|
16
|
+
* 2 16 row1
|
|
17
|
+
* 4 32 row2
|
|
18
|
+
* 64 128 row3
|
|
19
|
+
*
|
|
20
|
+
* For a plot of W cols x H rows we get W*2 x H*4 virtual pixels.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import React, { ReactNode, useMemo } from 'react'
|
|
24
|
+
import { Renderable, RGBA } from '@opentui/core'
|
|
25
|
+
import type { RenderableOptions, RenderContext } from '@opentui/core'
|
|
26
|
+
import type { OptimizedBuffer } from '@opentui/core'
|
|
27
|
+
import { extend } from '@opentui/react'
|
|
28
|
+
import { useTheme, getThemePalette } from 'termcast/src/theme'
|
|
29
|
+
import { Color, resolveColor } from 'termcast/src/colors'
|
|
30
|
+
|
|
31
|
+
// ── Graph variant ────────────────────────────────────────────────────
|
|
32
|
+
// Three rendering modes for the plot area:
|
|
33
|
+
// - 'area': braille dots filling area under curve (2×4 sub-pixel resolution)
|
|
34
|
+
// - 'filled': solid block characters (2× vertical sub-row resolution)
|
|
35
|
+
// - 'striped': all columns filled, alternating between two colors
|
|
36
|
+
// (pass 'transparent' for one color to get gap-style bars)
|
|
37
|
+
|
|
38
|
+
export type GraphVariant = 'area' | 'filled' | 'striped'
|
|
39
|
+
|
|
40
|
+
// ── Block characters for Filled/Striped modes ───────────────────────
|
|
41
|
+
// We use ▀/▄ with fg+bg color encoding to eliminate the tiny gaps
|
|
42
|
+
// some terminals show between adjacent █ rows.
|
|
43
|
+
// ▀ = top half drawn with fg, bottom half shows bg
|
|
44
|
+
// ▄ = bottom half drawn with fg, top half shows bg
|
|
45
|
+
// ▁ = lower 1/8 block, used as a thin baseline for zero/minimum values
|
|
46
|
+
const UPPER_HALF = '▀' // U+2580
|
|
47
|
+
const LOWER_HALF = '▄' // U+2584
|
|
48
|
+
const LOWER_EIGHTH = '▁' // U+2581
|
|
49
|
+
|
|
50
|
+
// ── Braille bit map ──────────────────────────────────────────────────
|
|
51
|
+
// Maps (subCol, subRow) to the braille bit for that dot position.
|
|
52
|
+
// subCol is 0 or 1, subRow is 0..3.
|
|
53
|
+
const BRAILLE_BITS: number[][] = [
|
|
54
|
+
// col 0 col 1
|
|
55
|
+
[1, 8], // row 0
|
|
56
|
+
[2, 16], // row 1
|
|
57
|
+
[4, 32], // row 2
|
|
58
|
+
[64, 128], // row 3
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
function brailleBit(subCol: number, subRow: number): number {
|
|
62
|
+
return BRAILLE_BITS[subRow]![subCol]!
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// ── Series data passed to the renderable ─────────────────────────────
|
|
66
|
+
|
|
67
|
+
export interface SeriesData {
|
|
68
|
+
data: number[]
|
|
69
|
+
color: string // hex color
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ── GraphPlotRenderable ──────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
export interface GraphPlotOptions extends RenderableOptions {
|
|
75
|
+
series?: SeriesData[]
|
|
76
|
+
xLabels?: string[]
|
|
77
|
+
yMin?: number
|
|
78
|
+
yMax?: number
|
|
79
|
+
yTicks?: number
|
|
80
|
+
yFormat?: (v: number) => string
|
|
81
|
+
axisColor?: string
|
|
82
|
+
variant?: GraphVariant
|
|
83
|
+
stripeColor1?: string // hex color for even columns in striped mode
|
|
84
|
+
stripeColor2?: string // hex color for odd columns in striped mode
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export class GraphPlotRenderable extends Renderable {
|
|
88
|
+
private _series: SeriesData[] = []
|
|
89
|
+
private _xLabels: string[] = []
|
|
90
|
+
private _yMin = 0
|
|
91
|
+
private _yMax = 100
|
|
92
|
+
private _yTicks = 5
|
|
93
|
+
private _yFormat: (v: number) => string = (v) => {
|
|
94
|
+
return v >= 1000 ? v.toFixed(0) : v.toFixed(1)
|
|
95
|
+
}
|
|
96
|
+
private _axisColor: string = '#666666'
|
|
97
|
+
private _variant: GraphVariant = 'area'
|
|
98
|
+
private _stripeColor1: string = '#0080FF'
|
|
99
|
+
private _stripeColor2: string = '#FF8000'
|
|
100
|
+
|
|
101
|
+
constructor(ctx: RenderContext, options: GraphPlotOptions) {
|
|
102
|
+
super(ctx, options)
|
|
103
|
+
if (options.series) this._series = options.series
|
|
104
|
+
if (options.xLabels) this._xLabels = options.xLabels
|
|
105
|
+
if (options.yMin !== undefined) this._yMin = options.yMin
|
|
106
|
+
if (options.yMax !== undefined) this._yMax = options.yMax
|
|
107
|
+
if (options.yTicks !== undefined) this._yTicks = options.yTicks
|
|
108
|
+
if (options.yFormat) this._yFormat = options.yFormat
|
|
109
|
+
if (options.axisColor) this._axisColor = options.axisColor
|
|
110
|
+
if (options.variant) this._variant = options.variant
|
|
111
|
+
if (options.stripeColor1) this._stripeColor1 = options.stripeColor1
|
|
112
|
+
if (options.stripeColor2) this._stripeColor2 = options.stripeColor2
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
set series(value: SeriesData[]) { this._series = value; this.requestRender() }
|
|
116
|
+
set xLabels(value: string[]) { this._xLabels = value; this.requestRender() }
|
|
117
|
+
set yMin(value: number) { this._yMin = value; this.requestRender() }
|
|
118
|
+
set yMax(value: number) { this._yMax = value; this.requestRender() }
|
|
119
|
+
set yTicks(value: number) { this._yTicks = value; this.requestRender() }
|
|
120
|
+
set yFormat(value: (v: number) => string) { this._yFormat = value; this.requestRender() }
|
|
121
|
+
set axisColor(value: string) { this._axisColor = value; this.requestRender() }
|
|
122
|
+
set variant(value: GraphVariant) { this._variant = value; this.requestRender() }
|
|
123
|
+
set stripeColor1(value: string) { this._stripeColor1 = value; this.requestRender() }
|
|
124
|
+
set stripeColor2(value: string) { this._stripeColor2 = value; this.requestRender() }
|
|
125
|
+
|
|
126
|
+
// ── Shared: compute layout and draw axes ─────────────────────
|
|
127
|
+
private computeLayout(): {
|
|
128
|
+
plotX: number; plotY: number; plotW: number; plotH: number
|
|
129
|
+
yAxisWidth: number; yLabels: string[]
|
|
130
|
+
} | null {
|
|
131
|
+
const totalW = this.width
|
|
132
|
+
const totalH = this.height
|
|
133
|
+
if (totalW <= 0 || totalH <= 0) return null
|
|
134
|
+
|
|
135
|
+
const yLabels: string[] = []
|
|
136
|
+
for (let i = 0; i < this._yTicks; i++) {
|
|
137
|
+
const value = this._yMin + (this._yMax - this._yMin) * (1 - i / (this._yTicks - 1))
|
|
138
|
+
yLabels.push(this._yFormat(value))
|
|
139
|
+
}
|
|
140
|
+
const yAxisWidth = Math.max(...yLabels.map((l) => l.length))
|
|
141
|
+
|
|
142
|
+
const plotX = this.x + yAxisWidth + 1
|
|
143
|
+
const plotY = this.y
|
|
144
|
+
const plotH = totalH - 1
|
|
145
|
+
const plotW = totalW - yAxisWidth - 1
|
|
146
|
+
if (plotW <= 0 || plotH <= 0) return null
|
|
147
|
+
|
|
148
|
+
return { plotX, plotY, plotW, plotH, yAxisWidth, yLabels }
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
private drawAxes(buffer: OptimizedBuffer, layout: {
|
|
152
|
+
plotX: number; plotY: number; plotW: number; plotH: number
|
|
153
|
+
yAxisWidth: number; yLabels: string[]
|
|
154
|
+
}): void {
|
|
155
|
+
const { plotX, plotY, plotW, plotH, yAxisWidth, yLabels } = layout
|
|
156
|
+
const axisRgba = RGBA.fromHex(this._axisColor)
|
|
157
|
+
|
|
158
|
+
// Y-axis labels + separator
|
|
159
|
+
const labelRows = new Set<number>()
|
|
160
|
+
for (let i = 0; i < this._yTicks; i++) {
|
|
161
|
+
const row = Math.round(plotY + (i / (this._yTicks - 1)) * (plotH - 1))
|
|
162
|
+
labelRows.add(row)
|
|
163
|
+
const label = yLabels[i]!
|
|
164
|
+
buffer.drawText(label, this.x + yAxisWidth - label.length, row, axisRgba)
|
|
165
|
+
buffer.drawText('│', this.x + yAxisWidth, row, axisRgba)
|
|
166
|
+
}
|
|
167
|
+
for (let row = plotY; row < plotY + plotH; row++) {
|
|
168
|
+
if (!labelRows.has(row)) {
|
|
169
|
+
buffer.drawText('│', this.x + yAxisWidth, row, axisRgba)
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// X-axis labels
|
|
174
|
+
if (this._xLabels.length > 0) {
|
|
175
|
+
const xAxisRow = plotY + plotH
|
|
176
|
+
const labelCount = this._xLabels.length
|
|
177
|
+
for (let i = 0; i < labelCount; i++) {
|
|
178
|
+
const label = this._xLabels[i]!
|
|
179
|
+
const labelX = plotX + Math.round((i / Math.max(1, labelCount - 1)) * (plotW - 1))
|
|
180
|
+
const centeredX = Math.max(plotX, Math.min(labelX - Math.floor(label.length / 2), plotX + plotW - label.length))
|
|
181
|
+
buffer.drawText(label, centeredX, xAxisRow, axisRgba)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ── Shared: interpolate line Y per column ────────────────────
|
|
187
|
+
// Returns an array where lineY[col] = the topmost virtual-row Y
|
|
188
|
+
// of the series line at that column. virtualRows is the total
|
|
189
|
+
// number of virtual rows (pixW for braille, plotW for block modes).
|
|
190
|
+
private computeLineYPerColumn({ series, colCount, virtualH }: {
|
|
191
|
+
series: { data: number[]; color: RGBA }
|
|
192
|
+
colCount: number
|
|
193
|
+
virtualH: number
|
|
194
|
+
}): Int32Array {
|
|
195
|
+
const yRange = this._yMax - this._yMin
|
|
196
|
+
const dataLen = series.data.length
|
|
197
|
+
const lineY = new Int32Array(colCount).fill(virtualH)
|
|
198
|
+
|
|
199
|
+
const pixelYs: number[] = series.data.map((v) => {
|
|
200
|
+
const normalized = (v - this._yMin) / yRange
|
|
201
|
+
return Math.round((1 - normalized) * (virtualH - 1))
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
for (let i = 0; i < dataLen - 1; i++) {
|
|
205
|
+
const x0 = Math.round((i / (dataLen - 1)) * (colCount - 1))
|
|
206
|
+
const y0 = pixelYs[i]!
|
|
207
|
+
const x1 = Math.round(((i + 1) / (dataLen - 1)) * (colCount - 1))
|
|
208
|
+
const y1 = pixelYs[i + 1]!
|
|
209
|
+
|
|
210
|
+
let dx = Math.abs(x1 - x0)
|
|
211
|
+
let dy = Math.abs(y1 - y0)
|
|
212
|
+
const sx = x0 < x1 ? 1 : -1
|
|
213
|
+
const sy = y0 < y1 ? 1 : -1
|
|
214
|
+
let err = dx - dy
|
|
215
|
+
let cx = x0
|
|
216
|
+
let cy = y0
|
|
217
|
+
|
|
218
|
+
while (true) {
|
|
219
|
+
if (cx >= 0 && cx < colCount && cy >= 0 && cy < virtualH) {
|
|
220
|
+
if (cy < lineY[cx]!) lineY[cx] = cy
|
|
221
|
+
}
|
|
222
|
+
if (cx === x1 && cy === y1) break
|
|
223
|
+
const e2 = 2 * err
|
|
224
|
+
if (e2 > -dy) { err -= dy; cx += sx }
|
|
225
|
+
if (e2 < dx) { err += dx; cy += sy }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (dataLen === 1) {
|
|
230
|
+
const px = Math.round(colCount / 2)
|
|
231
|
+
const py = pixelYs[0]!
|
|
232
|
+
if (px >= 0 && px < colCount && py >= 0 && py < virtualH) {
|
|
233
|
+
lineY[px] = py
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return lineY
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ── Style: Area (braille) ────────────────────────────────────
|
|
241
|
+
private renderArea(buffer: OptimizedBuffer, plotX: number, plotY: number, plotW: number, plotH: number): void {
|
|
242
|
+
const transparent = RGBA.fromValues(0, 0, 0, 0)
|
|
243
|
+
const pixW = plotW * 2
|
|
244
|
+
const pixH = plotH * 4
|
|
245
|
+
const yRange = this._yMax - this._yMin
|
|
246
|
+
if (yRange === 0) return
|
|
247
|
+
|
|
248
|
+
const cellCount = plotW * plotH
|
|
249
|
+
const cellBits = new Uint8Array(cellCount)
|
|
250
|
+
const cellColors: RGBA[] = Array.from({ length: cellCount }, () => transparent)
|
|
251
|
+
|
|
252
|
+
for (const series of this._series) {
|
|
253
|
+
if (series.data.length === 0) continue
|
|
254
|
+
const seriesColor = RGBA.fromHex(series.color)
|
|
255
|
+
|
|
256
|
+
const lineY = this.computeLineYPerColumn({
|
|
257
|
+
series: { data: series.data, color: seriesColor },
|
|
258
|
+
colCount: pixW,
|
|
259
|
+
virtualH: pixH,
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
for (let px = 0; px < pixW; px++) {
|
|
263
|
+
const topY = lineY[px]!
|
|
264
|
+
if (topY >= pixH) continue
|
|
265
|
+
for (let py = topY; py < pixH; py++) {
|
|
266
|
+
const cellX = Math.floor(px / 2)
|
|
267
|
+
const cellY = Math.floor(py / 4)
|
|
268
|
+
const subCol = px % 2
|
|
269
|
+
const subRow = py % 4
|
|
270
|
+
const cellIdx = cellY * plotW + cellX
|
|
271
|
+
if (cellIdx >= 0 && cellIdx < cellCount) {
|
|
272
|
+
cellBits[cellIdx]! |= brailleBit(subCol, subRow)
|
|
273
|
+
cellColors[cellIdx] = seriesColor
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (let cy = 0; cy < plotH; cy++) {
|
|
280
|
+
for (let cx = 0; cx < plotW; cx++) {
|
|
281
|
+
const cellIdx = cy * plotW + cx
|
|
282
|
+
const bits = cellBits[cellIdx]!
|
|
283
|
+
if (bits === 0) continue
|
|
284
|
+
buffer.setCell(plotX + cx, plotY + cy, String.fromCharCode(0x2800 + bits), cellColors[cellIdx]!, transparent)
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// ── Style: Filled / Striped (block characters) ───────────────
|
|
290
|
+
// Always uses ▀ (upper-half block) with fg=top color, bg=bottom color.
|
|
291
|
+
// This eliminates the tiny gaps some terminals show between adjacent █ rows.
|
|
292
|
+
// Filled: every column uses the series color.
|
|
293
|
+
// Striped: all columns filled, even cols = stripeColor1, odd = stripeColor2.
|
|
294
|
+
// Pass a transparent color to skip those columns (gap-style bars).
|
|
295
|
+
private renderBlock(buffer: OptimizedBuffer, plotX: number, plotY: number, plotW: number, plotH: number, striped: boolean): void {
|
|
296
|
+
const transparent = RGBA.fromValues(0, 0, 0, 0)
|
|
297
|
+
const virtualH = plotH * 2 // 2 sub-rows per terminal row
|
|
298
|
+
const yRange = this._yMax - this._yMin
|
|
299
|
+
if (yRange === 0) return
|
|
300
|
+
|
|
301
|
+
const stripe1 = RGBA.fromHex(this._stripeColor1)
|
|
302
|
+
const stripe2 = RGBA.fromHex(this._stripeColor2)
|
|
303
|
+
|
|
304
|
+
for (const series of this._series) {
|
|
305
|
+
if (series.data.length === 0) continue
|
|
306
|
+
const seriesColor = RGBA.fromHex(series.color)
|
|
307
|
+
|
|
308
|
+
const lineY = this.computeLineYPerColumn({
|
|
309
|
+
series: { data: series.data, color: seriesColor },
|
|
310
|
+
colCount: plotW,
|
|
311
|
+
virtualH,
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
for (let col = 0; col < plotW; col++) {
|
|
315
|
+
// Determine fill color for this column
|
|
316
|
+
const fillColor = striped
|
|
317
|
+
? (col % 2 === 0 ? stripe1 : stripe2)
|
|
318
|
+
: seriesColor
|
|
319
|
+
|
|
320
|
+
// Skip transparent columns (allows gap-style bars in striped mode)
|
|
321
|
+
if (fillColor.a === 0) continue
|
|
322
|
+
|
|
323
|
+
const topVRow = lineY[col]!
|
|
324
|
+
if (topVRow >= virtualH) continue
|
|
325
|
+
|
|
326
|
+
// Fill from topVRow down to virtualH-1 using ▀/▄ with fg+bg encoding.
|
|
327
|
+
// ▀: fg paints top half, bg paints bottom half.
|
|
328
|
+
// ▄: fg paints bottom half, bg paints top half.
|
|
329
|
+
// We never set fg=transparent on a visible glyph part (would show as black).
|
|
330
|
+
for (let row = 0; row < plotH; row++) {
|
|
331
|
+
const vTop = row * 2 // virtual row for top half
|
|
332
|
+
const vBot = row * 2 + 1 // virtual row for bottom half
|
|
333
|
+
const topFilled = vTop >= topVRow
|
|
334
|
+
const botFilled = vBot >= topVRow
|
|
335
|
+
|
|
336
|
+
if (!topFilled && !botFilled) continue
|
|
337
|
+
|
|
338
|
+
if (topFilled && botFilled) {
|
|
339
|
+
// Both halves: ▀ with fg=color, bg=color → seamless full block
|
|
340
|
+
buffer.setCell(plotX + col, plotY + row, UPPER_HALF, fillColor, fillColor)
|
|
341
|
+
} else if (topFilled) {
|
|
342
|
+
// Top only: ▀ with fg=color, bg=transparent
|
|
343
|
+
buffer.setCell(plotX + col, plotY + row, UPPER_HALF, fillColor, transparent)
|
|
344
|
+
} else if (topVRow >= virtualH - 1) {
|
|
345
|
+
// Minimum fill: only the very last virtual row is filled (zero/min value).
|
|
346
|
+
// Use ▁ (lower 1/8 block) for a thin baseline indicator.
|
|
347
|
+
buffer.setCell(plotX + col, plotY + row, LOWER_EIGHTH, fillColor, transparent)
|
|
348
|
+
} else {
|
|
349
|
+
// Bottom only: ▄ with fg=color, bg=transparent
|
|
350
|
+
buffer.setCell(plotX + col, plotY + row, LOWER_HALF, fillColor, transparent)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// ── Main render ──────────────────────────────────────────────
|
|
358
|
+
protected renderSelf(buffer: OptimizedBuffer): void {
|
|
359
|
+
const layout = this.computeLayout()
|
|
360
|
+
if (!layout) return
|
|
361
|
+
const { plotX, plotY, plotW, plotH } = layout
|
|
362
|
+
|
|
363
|
+
this.drawAxes(buffer, layout)
|
|
364
|
+
|
|
365
|
+
const yRange = this._yMax - this._yMin
|
|
366
|
+
if (yRange === 0 || this._series.length === 0) return
|
|
367
|
+
|
|
368
|
+
switch (this._variant) {
|
|
369
|
+
case 'area': {
|
|
370
|
+
this.renderArea(buffer, plotX, plotY, plotW, plotH)
|
|
371
|
+
break
|
|
372
|
+
}
|
|
373
|
+
case 'filled': {
|
|
374
|
+
this.renderBlock(buffer, plotX, plotY, plotW, plotH, false)
|
|
375
|
+
break
|
|
376
|
+
}
|
|
377
|
+
case 'striped': {
|
|
378
|
+
this.renderBlock(buffer, plotX, plotY, plotW, plotH, true)
|
|
379
|
+
break
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ── Register the custom renderable ───────────────────────────────────
|
|
386
|
+
|
|
387
|
+
extend({ 'graph-plot': GraphPlotRenderable })
|
|
388
|
+
|
|
389
|
+
declare module '@opentui/react' {
|
|
390
|
+
interface OpenTUIComponents {
|
|
391
|
+
'graph-plot': typeof GraphPlotRenderable
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// ── Graph.Line (data-only child, renders null) ───────────────────────
|
|
396
|
+
|
|
397
|
+
export interface GraphLineProps {
|
|
398
|
+
/** Y-values for this series */
|
|
399
|
+
data: number[]
|
|
400
|
+
/** Line color */
|
|
401
|
+
color?: Color.ColorLike
|
|
402
|
+
/** Series label (for future legend) */
|
|
403
|
+
title?: string
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const GraphLine = (_props: GraphLineProps): any => {
|
|
407
|
+
return null
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// ── Graph React component ────────────────────────────────────────────
|
|
411
|
+
|
|
412
|
+
export interface GraphProps {
|
|
413
|
+
/** Height of the graph in terminal rows (default: 15) */
|
|
414
|
+
height?: number
|
|
415
|
+
/** X-axis labels */
|
|
416
|
+
xLabels?: string[]
|
|
417
|
+
/** Manual Y-axis range [min, max] (default: auto from data) */
|
|
418
|
+
yRange?: [number, number]
|
|
419
|
+
/** Number of Y-axis tick labels (default: 5) */
|
|
420
|
+
yTicks?: number
|
|
421
|
+
/** Custom Y-axis label formatter */
|
|
422
|
+
yFormat?: (v: number) => string
|
|
423
|
+
/** Rendering variant: 'area' (braille), 'filled' (blocks), 'striped' (alternating colors) */
|
|
424
|
+
variant?: GraphVariant
|
|
425
|
+
/** Two alternating colors for 'striped' variant [even, odd]. Defaults to [theme.primary, theme.accent].
|
|
426
|
+
* Pass 'transparent' for one to get gap-style bars. */
|
|
427
|
+
stripeColors?: [Color.ColorLike, Color.ColorLike]
|
|
428
|
+
/** Graph.Line children */
|
|
429
|
+
children: ReactNode
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
interface GraphType {
|
|
433
|
+
(props: GraphProps): any
|
|
434
|
+
Line: (props: GraphLineProps) => any
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const Graph: GraphType = (props) => {
|
|
438
|
+
const theme = useTheme()
|
|
439
|
+
const { height = 15, xLabels = [], yRange, yTicks = 5, yFormat, variant = 'area', stripeColors, children } = props
|
|
440
|
+
|
|
441
|
+
const palette = getThemePalette(theme)
|
|
442
|
+
|
|
443
|
+
// Collect series data from Graph.Line children
|
|
444
|
+
const series = useMemo<SeriesData[]>(() => {
|
|
445
|
+
const result: SeriesData[] = []
|
|
446
|
+
let colorIndex = 0
|
|
447
|
+
React.Children.forEach(children, (child) => {
|
|
448
|
+
if (!React.isValidElement(child)) return
|
|
449
|
+
const childProps = child.props as GraphLineProps
|
|
450
|
+
if (!childProps.data) return
|
|
451
|
+
|
|
452
|
+
const color = resolveColor(childProps.color) || palette[colorIndex % palette.length]!
|
|
453
|
+
result.push({
|
|
454
|
+
data: childProps.data,
|
|
455
|
+
color,
|
|
456
|
+
})
|
|
457
|
+
colorIndex++
|
|
458
|
+
})
|
|
459
|
+
return result
|
|
460
|
+
}, [children, palette])
|
|
461
|
+
|
|
462
|
+
// Auto-compute Y range if not provided
|
|
463
|
+
const computedYRange = useMemo<[number, number]>(() => {
|
|
464
|
+
if (yRange) return yRange
|
|
465
|
+
let min = Infinity
|
|
466
|
+
let max = -Infinity
|
|
467
|
+
for (const s of series) {
|
|
468
|
+
for (const v of s.data) {
|
|
469
|
+
if (v < min) min = v
|
|
470
|
+
if (v > max) max = v
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (min === Infinity) return [0, 100]
|
|
474
|
+
// Add small padding so lines don't touch edges
|
|
475
|
+
const padding = (max - min) * 0.05
|
|
476
|
+
return [min - padding, max + padding]
|
|
477
|
+
}, [series, yRange])
|
|
478
|
+
|
|
479
|
+
// Resolve stripe colors (defaults to theme primary + accent)
|
|
480
|
+
const resolvedStripe1 = resolveColor(stripeColors?.[0]) || theme.primary
|
|
481
|
+
const resolvedStripe2 = resolveColor(stripeColors?.[1]) || theme.accent
|
|
482
|
+
|
|
483
|
+
// Total height = plot rows + 1 for x-axis labels
|
|
484
|
+
const totalHeight = height + (xLabels.length > 0 ? 1 : 0)
|
|
485
|
+
|
|
486
|
+
return (
|
|
487
|
+
<graph-plot
|
|
488
|
+
width="100%"
|
|
489
|
+
height={totalHeight}
|
|
490
|
+
series={series}
|
|
491
|
+
xLabels={xLabels}
|
|
492
|
+
yMin={computedYRange[0]}
|
|
493
|
+
yMax={computedYRange[1]}
|
|
494
|
+
yTicks={yTicks}
|
|
495
|
+
yFormat={yFormat}
|
|
496
|
+
axisColor={theme.textMuted}
|
|
497
|
+
variant={variant}
|
|
498
|
+
stripeColor1={resolvedStripe1}
|
|
499
|
+
stripeColor2={resolvedStripe2}
|
|
500
|
+
/>
|
|
501
|
+
)
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
Graph.Line = GraphLine
|
|
505
|
+
|
|
506
|
+
export { Graph }
|
package/src/components/icon.tsx
CHANGED
|
@@ -45,8 +45,8 @@ const ICON_MAP: Record<string, string> = {
|
|
|
45
45
|
'at-symbol-16': '@',
|
|
46
46
|
'band-aid-16': '╋',
|
|
47
47
|
'bank-note-16': '¤',
|
|
48
|
-
'bar-chart-16': '▊',
|
|
49
|
-
'bar-code-16': '▐',
|
|
48
|
+
// 'bar-chart-16': '▊',
|
|
49
|
+
// 'bar-code-16': '▐',
|
|
50
50
|
'bath-tub-16': '▬',
|
|
51
51
|
'battery-16': '▮',
|
|
52
52
|
'battery-charging-16': '▮',
|
|
@@ -188,7 +188,7 @@ const ICON_MAP: Record<string, string> = {
|
|
|
188
188
|
'key-16': '⊢',
|
|
189
189
|
'keyboard-16': '⌗',
|
|
190
190
|
'layers-16': '◫',
|
|
191
|
-
'leaderboard-16': '▊',
|
|
191
|
+
// 'leaderboard-16': '▊',
|
|
192
192
|
'leaf-16': '∿',
|
|
193
193
|
'light-bulb-16': '※',
|
|
194
194
|
'light-bulb-off-16': '※',
|
|
@@ -288,8 +288,8 @@ const ICON_MAP: Record<string, string> = {
|
|
|
288
288
|
'shuffle-16': '⇝',
|
|
289
289
|
'signal-0-16': '▁',
|
|
290
290
|
'signal-1-16': '▂',
|
|
291
|
-
'signal-2-16': '▄',
|
|
292
|
-
'signal-3-16': '█',
|
|
291
|
+
// 'signal-2-16': '▄',
|
|
292
|
+
// 'signal-3-16': '█',
|
|
293
293
|
'snippets-16': '⊠',
|
|
294
294
|
'snowflake-16': '※',
|
|
295
295
|
'soccer-ball-16': '◎',
|