@promptctl/rich-js 0.10.0 → 0.12.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/LICENSE +21 -0
- package/README.md +20 -110
- package/THIRD-PARTY-NOTICES +81 -0
- package/dist/core/box.d.ts +0 -1
- package/dist/core/box.js +0 -1
- package/dist/core/cells.d.ts +0 -1
- package/dist/core/cells.js +0 -1
- package/dist/core/color.d.ts +6 -1
- package/dist/core/color.js +6 -1
- package/dist/core/console.d.ts +13 -4
- package/dist/core/console.js +78 -87
- package/dist/core/emoji.d.ts +0 -1
- package/dist/core/emoji.js +0 -1
- package/dist/core/export-html.d.ts +24 -0
- package/dist/core/export-html.js +106 -0
- package/dist/core/export-lines.d.ts +121 -0
- package/dist/core/export-lines.js +141 -0
- package/dist/core/highlighter.d.ts +0 -1
- package/dist/core/highlighter.js +0 -1
- package/dist/core/json.d.ts +0 -1
- package/dist/core/json.js +0 -1
- package/dist/core/markup.d.ts +33 -2
- package/dist/core/markup.js +128 -16
- package/dist/core/measure.d.ts +0 -1
- package/dist/core/measure.js +0 -1
- package/dist/core/oklch.d.ts +34 -1
- package/dist/core/oklch.js +52 -5
- package/dist/core/osc8.d.ts +68 -0
- package/dist/core/osc8.js +90 -0
- package/dist/core/pretty.d.ts +21 -2
- package/dist/core/pretty.js +90 -16
- package/dist/core/protocol.d.ts +18 -2
- package/dist/core/protocol.js +0 -1
- package/dist/core/render.d.ts +0 -1
- package/dist/core/render.js +3 -10
- package/dist/core/segment.d.ts +0 -1
- package/dist/core/segment.js +0 -1
- package/dist/core/spinnerData.d.ts +0 -1
- package/dist/core/spinnerData.js +0 -1
- package/dist/core/strip.d.ts +19 -3
- package/dist/core/strip.js +58 -11
- package/dist/core/style.d.ts +3 -8
- package/dist/core/style.js +85 -68
- package/dist/core/subscription.d.ts +0 -1
- package/dist/core/subscription.js +0 -1
- package/dist/core/text.d.ts +1 -8
- package/dist/core/text.js +28 -31
- package/dist/core/wrap.d.ts +0 -1
- package/dist/core/wrap.js +0 -1
- package/dist/host/host-stream.d.ts +0 -1
- package/dist/host/host-stream.js +0 -1
- package/dist/host/index.d.ts +0 -1
- package/dist/host/index.js +0 -1
- package/dist/host/terminal-host.d.ts +0 -1
- package/dist/host/terminal-host.js +0 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.js +4 -3
- package/dist/node/prompt.d.ts +0 -1
- package/dist/node/prompt.js +0 -1
- package/dist/node/save.d.ts +2 -7
- package/dist/node/save.js +0 -1
- package/dist/node/terminal-host.d.ts +0 -1
- package/dist/node/terminal-host.js +0 -1
- package/dist/node/traceback.d.ts +0 -1
- package/dist/node/traceback.js +0 -1
- package/dist/renderables/align.d.ts +0 -1
- package/dist/renderables/align.js +0 -1
- package/dist/renderables/columns.d.ts +12 -1
- package/dist/renderables/columns.js +29 -5
- package/dist/renderables/constrain.d.ts +0 -1
- package/dist/renderables/constrain.js +0 -1
- package/dist/renderables/flexStrip.d.ts +0 -1
- package/dist/renderables/flexStrip.js +0 -1
- package/dist/renderables/group.d.ts +0 -1
- package/dist/renderables/group.js +0 -1
- package/dist/renderables/layout.d.ts +0 -1
- package/dist/renderables/layout.js +20 -7
- package/dist/renderables/live.d.ts +0 -1
- package/dist/renderables/live.js +0 -1
- package/dist/renderables/markdown.d.ts +0 -1
- package/dist/renderables/markdown.js +0 -1
- package/dist/renderables/padding.d.ts +0 -1
- package/dist/renderables/padding.js +0 -1
- package/dist/renderables/panel.d.ts +0 -1
- package/dist/renderables/panel.js +12 -4
- package/dist/renderables/progress.d.ts +0 -1
- package/dist/renderables/progress.js +7 -2
- package/dist/renderables/progressBar.d.ts +0 -1
- package/dist/renderables/progressBar.js +0 -1
- package/dist/renderables/prompt.d.ts +0 -1
- package/dist/renderables/prompt.js +0 -1
- package/dist/renderables/rule.d.ts +0 -1
- package/dist/renderables/rule.js +0 -1
- package/dist/renderables/spinner.d.ts +0 -1
- package/dist/renderables/spinner.js +0 -1
- package/dist/renderables/status.d.ts +0 -1
- package/dist/renderables/status.js +0 -1
- package/dist/renderables/syntax.d.ts +0 -1
- package/dist/renderables/syntax.js +0 -1
- package/dist/renderables/table.d.ts +0 -1
- package/dist/renderables/table.js +8 -3
- package/dist/renderables/traceback.d.ts +8 -7
- package/dist/renderables/traceback.js +14 -10
- package/dist/renderables/tree.d.ts +0 -1
- package/dist/renderables/tree.js +0 -1
- package/dist/template-bindings/color-funcs.d.ts +0 -1
- package/dist/template-bindings/color-funcs.js +0 -1
- package/dist/template-bindings/helpers.d.ts +0 -1
- package/dist/template-bindings/helpers.js +0 -1
- package/dist/template-bindings/index.d.ts +0 -1
- package/dist/template-bindings/index.js +0 -1
- package/dist/template-bindings/palette-funcs.d.ts +0 -1
- package/dist/template-bindings/palette-funcs.js +0 -1
- package/dist/template-bindings/style-funcs.d.ts +0 -1
- package/dist/template-bindings/style-funcs.js +0 -1
- package/dist/themes/buildPalette.d.ts +0 -1
- package/dist/themes/buildPalette.js +0 -1
- package/dist/themes/colorMath.d.ts +0 -1
- package/dist/themes/colorMath.js +0 -1
- package/dist/themes/colorRef.d.ts +0 -1
- package/dist/themes/colorRef.js +0 -1
- package/dist/themes/data/atom-one-dark.d.ts +0 -1
- package/dist/themes/data/atom-one-dark.js +0 -1
- package/dist/themes/data/atom-one-light.d.ts +0 -1
- package/dist/themes/data/atom-one-light.js +0 -1
- package/dist/themes/data/catppuccin-frappe.d.ts +0 -1
- package/dist/themes/data/catppuccin-frappe.js +0 -1
- package/dist/themes/data/catppuccin-latte.d.ts +0 -1
- package/dist/themes/data/catppuccin-latte.js +0 -1
- package/dist/themes/data/catppuccin-macchiato.d.ts +0 -1
- package/dist/themes/data/catppuccin-macchiato.js +0 -1
- package/dist/themes/data/catppuccin-mocha.d.ts +0 -1
- package/dist/themes/data/catppuccin-mocha.js +0 -1
- package/dist/themes/data/cyberpunk.d.ts +0 -1
- package/dist/themes/data/cyberpunk.js +0 -1
- package/dist/themes/data/default.d.ts +0 -1
- package/dist/themes/data/default.js +0 -1
- package/dist/themes/data/dracula.d.ts +0 -1
- package/dist/themes/data/dracula.js +0 -1
- package/dist/themes/data/flexoki.d.ts +0 -1
- package/dist/themes/data/flexoki.js +0 -1
- package/dist/themes/data/gruvbox.d.ts +0 -1
- package/dist/themes/data/gruvbox.js +0 -1
- package/dist/themes/data/index.d.ts +0 -1
- package/dist/themes/data/index.js +0 -1
- package/dist/themes/data/monokai.d.ts +0 -1
- package/dist/themes/data/monokai.js +0 -1
- package/dist/themes/data/nord.d.ts +0 -1
- package/dist/themes/data/nord.js +0 -1
- package/dist/themes/data/rose-pine-dawn.d.ts +0 -1
- package/dist/themes/data/rose-pine-dawn.js +0 -1
- package/dist/themes/data/rose-pine-moon.d.ts +0 -1
- package/dist/themes/data/rose-pine-moon.js +0 -1
- package/dist/themes/data/rose-pine.d.ts +0 -1
- package/dist/themes/data/rose-pine.js +0 -1
- package/dist/themes/data/solarized-dark.d.ts +0 -1
- package/dist/themes/data/solarized-dark.js +0 -1
- package/dist/themes/data/solarized-light.d.ts +0 -1
- package/dist/themes/data/solarized-light.js +0 -1
- package/dist/themes/data/svg-export.d.ts +0 -1
- package/dist/themes/data/svg-export.js +0 -1
- package/dist/themes/data/textual-ansi.d.ts +0 -1
- package/dist/themes/data/textual-ansi.js +0 -1
- package/dist/themes/data/textual-dark.d.ts +0 -1
- package/dist/themes/data/textual-dark.js +0 -1
- package/dist/themes/data/textual-light.d.ts +0 -1
- package/dist/themes/data/textual-light.js +0 -1
- package/dist/themes/data/tokyo-night.d.ts +0 -1
- package/dist/themes/data/tokyo-night.js +0 -1
- package/dist/themes/data/types.d.ts +0 -1
- package/dist/themes/data/types.js +0 -1
- package/dist/themes/palette.d.ts +0 -1
- package/dist/themes/palette.js +0 -1
- package/dist/themes/ramp.d.ts +0 -1
- package/dist/themes/ramp.js +0 -1
- package/dist/themes/registry.d.ts +0 -1
- package/dist/themes/registry.js +0 -1
- package/dist/themes/terminalThemes.d.ts +0 -1
- package/dist/themes/terminalThemes.js +0 -1
- package/dist/themes/transpose.d.ts +0 -1
- package/dist/themes/transpose.js +0 -1
- package/dist/widgets/button.d.ts +0 -1
- package/dist/widgets/button.js +0 -1
- package/dist/widgets/checkbox.d.ts +0 -1
- package/dist/widgets/checkbox.js +0 -1
- package/dist/widgets/dropdown.d.ts +0 -1
- package/dist/widgets/dropdown.js +0 -1
- package/dist/widgets/event-router.d.ts +0 -1
- package/dist/widgets/event-router.js +0 -1
- package/dist/widgets/focus-manager.d.ts +0 -1
- package/dist/widgets/focus-manager.js +0 -1
- package/dist/widgets/index.d.ts +0 -1
- package/dist/widgets/index.js +0 -1
- package/dist/widgets/screen.d.ts +0 -1
- package/dist/widgets/screen.js +0 -1
- package/dist/widgets/slider.d.ts +0 -1
- package/dist/widgets/slider.js +0 -1
- package/dist/widgets/static-item.d.ts +0 -1
- package/dist/widgets/static-item.js +0 -1
- package/dist/widgets/text-input.d.ts +0 -1
- package/dist/widgets/text-input.js +0 -1
- package/dist/widgets/toggle.d.ts +0 -1
- package/dist/widgets/toggle.js +0 -1
- package/dist/widgets/types.d.ts +0 -1
- package/dist/widgets/types.js +0 -1
- package/dist/widgets/widget-base.d.ts +0 -1
- package/dist/widgets/widget-base.js +0 -1
- package/package.json +4 -3
- package/dist/core/box.d.ts.map +0 -1
- package/dist/core/box.js.map +0 -1
- package/dist/core/cells.d.ts.map +0 -1
- package/dist/core/cells.js.map +0 -1
- package/dist/core/color.d.ts.map +0 -1
- package/dist/core/color.js.map +0 -1
- package/dist/core/console.d.ts.map +0 -1
- package/dist/core/console.js.map +0 -1
- package/dist/core/emoji.d.ts.map +0 -1
- package/dist/core/emoji.js.map +0 -1
- package/dist/core/highlighter.d.ts.map +0 -1
- package/dist/core/highlighter.js.map +0 -1
- package/dist/core/json.d.ts.map +0 -1
- package/dist/core/json.js.map +0 -1
- package/dist/core/markup.d.ts.map +0 -1
- package/dist/core/markup.js.map +0 -1
- package/dist/core/measure.d.ts.map +0 -1
- package/dist/core/measure.js.map +0 -1
- package/dist/core/oklch.d.ts.map +0 -1
- package/dist/core/oklch.js.map +0 -1
- package/dist/core/pretty.d.ts.map +0 -1
- package/dist/core/pretty.js.map +0 -1
- package/dist/core/protocol.d.ts.map +0 -1
- package/dist/core/protocol.js.map +0 -1
- package/dist/core/render.d.ts.map +0 -1
- package/dist/core/render.js.map +0 -1
- package/dist/core/sanitize.d.ts +0 -16
- package/dist/core/sanitize.d.ts.map +0 -1
- package/dist/core/sanitize.js +0 -19
- package/dist/core/sanitize.js.map +0 -1
- package/dist/core/segment.d.ts.map +0 -1
- package/dist/core/segment.js.map +0 -1
- package/dist/core/spinnerData.d.ts.map +0 -1
- package/dist/core/spinnerData.js.map +0 -1
- package/dist/core/strip.d.ts.map +0 -1
- package/dist/core/strip.js.map +0 -1
- package/dist/core/style.d.ts.map +0 -1
- package/dist/core/style.js.map +0 -1
- package/dist/core/subscription.d.ts.map +0 -1
- package/dist/core/subscription.js.map +0 -1
- package/dist/core/text.d.ts.map +0 -1
- package/dist/core/text.js.map +0 -1
- package/dist/core/wrap.d.ts.map +0 -1
- package/dist/core/wrap.js.map +0 -1
- package/dist/host/host-stream.d.ts.map +0 -1
- package/dist/host/host-stream.js.map +0 -1
- package/dist/host/index.d.ts.map +0 -1
- package/dist/host/index.js.map +0 -1
- package/dist/host/terminal-host.d.ts.map +0 -1
- package/dist/host/terminal-host.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/node/prompt.d.ts.map +0 -1
- package/dist/node/prompt.js.map +0 -1
- package/dist/node/save.d.ts.map +0 -1
- package/dist/node/save.js.map +0 -1
- package/dist/node/terminal-host.d.ts.map +0 -1
- package/dist/node/terminal-host.js.map +0 -1
- package/dist/node/traceback.d.ts.map +0 -1
- package/dist/node/traceback.js.map +0 -1
- package/dist/renderables/align.d.ts.map +0 -1
- package/dist/renderables/align.js.map +0 -1
- package/dist/renderables/columns.d.ts.map +0 -1
- package/dist/renderables/columns.js.map +0 -1
- package/dist/renderables/constrain.d.ts.map +0 -1
- package/dist/renderables/constrain.js.map +0 -1
- package/dist/renderables/flexStrip.d.ts.map +0 -1
- package/dist/renderables/flexStrip.js.map +0 -1
- package/dist/renderables/group.d.ts.map +0 -1
- package/dist/renderables/group.js.map +0 -1
- package/dist/renderables/layout.d.ts.map +0 -1
- package/dist/renderables/layout.js.map +0 -1
- package/dist/renderables/live.d.ts.map +0 -1
- package/dist/renderables/live.js.map +0 -1
- package/dist/renderables/markdown.d.ts.map +0 -1
- package/dist/renderables/markdown.js.map +0 -1
- package/dist/renderables/padding.d.ts.map +0 -1
- package/dist/renderables/padding.js.map +0 -1
- package/dist/renderables/panel.d.ts.map +0 -1
- package/dist/renderables/panel.js.map +0 -1
- package/dist/renderables/progress.d.ts.map +0 -1
- package/dist/renderables/progress.js.map +0 -1
- package/dist/renderables/progressBar.d.ts.map +0 -1
- package/dist/renderables/progressBar.js.map +0 -1
- package/dist/renderables/prompt.d.ts.map +0 -1
- package/dist/renderables/prompt.js.map +0 -1
- package/dist/renderables/rule.d.ts.map +0 -1
- package/dist/renderables/rule.js.map +0 -1
- package/dist/renderables/spinner.d.ts.map +0 -1
- package/dist/renderables/spinner.js.map +0 -1
- package/dist/renderables/status.d.ts.map +0 -1
- package/dist/renderables/status.js.map +0 -1
- package/dist/renderables/syntax.d.ts.map +0 -1
- package/dist/renderables/syntax.js.map +0 -1
- package/dist/renderables/table.d.ts.map +0 -1
- package/dist/renderables/table.js.map +0 -1
- package/dist/renderables/traceback.d.ts.map +0 -1
- package/dist/renderables/traceback.js.map +0 -1
- package/dist/renderables/tree.d.ts.map +0 -1
- package/dist/renderables/tree.js.map +0 -1
- package/dist/template-bindings/color-funcs.d.ts.map +0 -1
- package/dist/template-bindings/color-funcs.js.map +0 -1
- package/dist/template-bindings/helpers.d.ts.map +0 -1
- package/dist/template-bindings/helpers.js.map +0 -1
- package/dist/template-bindings/index.d.ts.map +0 -1
- package/dist/template-bindings/index.js.map +0 -1
- package/dist/template-bindings/palette-funcs.d.ts.map +0 -1
- package/dist/template-bindings/palette-funcs.js.map +0 -1
- package/dist/template-bindings/style-funcs.d.ts.map +0 -1
- package/dist/template-bindings/style-funcs.js.map +0 -1
- package/dist/themes/buildPalette.d.ts.map +0 -1
- package/dist/themes/buildPalette.js.map +0 -1
- package/dist/themes/colorMath.d.ts.map +0 -1
- package/dist/themes/colorMath.js.map +0 -1
- package/dist/themes/colorRef.d.ts.map +0 -1
- package/dist/themes/colorRef.js.map +0 -1
- package/dist/themes/data/atom-one-dark.d.ts.map +0 -1
- package/dist/themes/data/atom-one-dark.js.map +0 -1
- package/dist/themes/data/atom-one-light.d.ts.map +0 -1
- package/dist/themes/data/atom-one-light.js.map +0 -1
- package/dist/themes/data/catppuccin-frappe.d.ts.map +0 -1
- package/dist/themes/data/catppuccin-frappe.js.map +0 -1
- package/dist/themes/data/catppuccin-latte.d.ts.map +0 -1
- package/dist/themes/data/catppuccin-latte.js.map +0 -1
- package/dist/themes/data/catppuccin-macchiato.d.ts.map +0 -1
- package/dist/themes/data/catppuccin-macchiato.js.map +0 -1
- package/dist/themes/data/catppuccin-mocha.d.ts.map +0 -1
- package/dist/themes/data/catppuccin-mocha.js.map +0 -1
- package/dist/themes/data/cyberpunk.d.ts.map +0 -1
- package/dist/themes/data/cyberpunk.js.map +0 -1
- package/dist/themes/data/default.d.ts.map +0 -1
- package/dist/themes/data/default.js.map +0 -1
- package/dist/themes/data/dracula.d.ts.map +0 -1
- package/dist/themes/data/dracula.js.map +0 -1
- package/dist/themes/data/flexoki.d.ts.map +0 -1
- package/dist/themes/data/flexoki.js.map +0 -1
- package/dist/themes/data/gruvbox.d.ts.map +0 -1
- package/dist/themes/data/gruvbox.js.map +0 -1
- package/dist/themes/data/index.d.ts.map +0 -1
- package/dist/themes/data/index.js.map +0 -1
- package/dist/themes/data/monokai.d.ts.map +0 -1
- package/dist/themes/data/monokai.js.map +0 -1
- package/dist/themes/data/nord.d.ts.map +0 -1
- package/dist/themes/data/nord.js.map +0 -1
- package/dist/themes/data/rose-pine-dawn.d.ts.map +0 -1
- package/dist/themes/data/rose-pine-dawn.js.map +0 -1
- package/dist/themes/data/rose-pine-moon.d.ts.map +0 -1
- package/dist/themes/data/rose-pine-moon.js.map +0 -1
- package/dist/themes/data/rose-pine.d.ts.map +0 -1
- package/dist/themes/data/rose-pine.js.map +0 -1
- package/dist/themes/data/solarized-dark.d.ts.map +0 -1
- package/dist/themes/data/solarized-dark.js.map +0 -1
- package/dist/themes/data/solarized-light.d.ts.map +0 -1
- package/dist/themes/data/solarized-light.js.map +0 -1
- package/dist/themes/data/svg-export.d.ts.map +0 -1
- package/dist/themes/data/svg-export.js.map +0 -1
- package/dist/themes/data/textual-ansi.d.ts.map +0 -1
- package/dist/themes/data/textual-ansi.js.map +0 -1
- package/dist/themes/data/textual-dark.d.ts.map +0 -1
- package/dist/themes/data/textual-dark.js.map +0 -1
- package/dist/themes/data/textual-light.d.ts.map +0 -1
- package/dist/themes/data/textual-light.js.map +0 -1
- package/dist/themes/data/tokyo-night.d.ts.map +0 -1
- package/dist/themes/data/tokyo-night.js.map +0 -1
- package/dist/themes/data/types.d.ts.map +0 -1
- package/dist/themes/data/types.js.map +0 -1
- package/dist/themes/palette.d.ts.map +0 -1
- package/dist/themes/palette.js.map +0 -1
- package/dist/themes/ramp.d.ts.map +0 -1
- package/dist/themes/ramp.js.map +0 -1
- package/dist/themes/registry.d.ts.map +0 -1
- package/dist/themes/registry.js.map +0 -1
- package/dist/themes/terminalThemes.d.ts.map +0 -1
- package/dist/themes/terminalThemes.js.map +0 -1
- package/dist/themes/transpose.d.ts.map +0 -1
- package/dist/themes/transpose.js.map +0 -1
- package/dist/widgets/button.d.ts.map +0 -1
- package/dist/widgets/button.js.map +0 -1
- package/dist/widgets/checkbox.d.ts.map +0 -1
- package/dist/widgets/checkbox.js.map +0 -1
- package/dist/widgets/dropdown.d.ts.map +0 -1
- package/dist/widgets/dropdown.js.map +0 -1
- package/dist/widgets/event-router.d.ts.map +0 -1
- package/dist/widgets/event-router.js.map +0 -1
- package/dist/widgets/focus-manager.d.ts.map +0 -1
- package/dist/widgets/focus-manager.js.map +0 -1
- package/dist/widgets/index.d.ts.map +0 -1
- package/dist/widgets/index.js.map +0 -1
- package/dist/widgets/screen.d.ts.map +0 -1
- package/dist/widgets/screen.js.map +0 -1
- package/dist/widgets/slider.d.ts.map +0 -1
- package/dist/widgets/slider.js.map +0 -1
- package/dist/widgets/static-item.d.ts.map +0 -1
- package/dist/widgets/static-item.js.map +0 -1
- package/dist/widgets/text-input.d.ts.map +0 -1
- package/dist/widgets/text-input.js.map +0 -1
- package/dist/widgets/toggle.d.ts.map +0 -1
- package/dist/widgets/toggle.js.map +0 -1
- package/dist/widgets/types.d.ts.map +0 -1
- package/dist/widgets/types.js.map +0 -1
- package/dist/widgets/widget-base.d.ts.map +0 -1
- package/dist/widgets/widget-base.js.map +0 -1
package/dist/core/markup.js
CHANGED
|
@@ -14,8 +14,9 @@
|
|
|
14
14
|
* (`[bold]a[italic]b[/bold]c[/italic]`) because a style is an annotation and
|
|
15
15
|
* annotations may overlap freely; a plugin tag is a replacement whose handler
|
|
16
16
|
* takes one contiguous `inner`, so an overlapping pair has no slice to hand it
|
|
17
|
-
* and is rejected with a `
|
|
17
|
+
* and is rejected with a `MarkupSyntaxError`.
|
|
18
18
|
*/
|
|
19
|
+
import { cellLen } from "./cells.js";
|
|
19
20
|
import { Style, StyleSyntaxError } from "./style.js";
|
|
20
21
|
import { RichText, Span } from "./text.js";
|
|
21
22
|
import { emojiReplace } from "./emoji.js";
|
|
@@ -45,6 +46,94 @@ export class MarkupError extends Error {
|
|
|
45
46
|
this.name = "MarkupError";
|
|
46
47
|
}
|
|
47
48
|
}
|
|
49
|
+
// --- MarkupSyntaxError ---
|
|
50
|
+
/**
|
|
51
|
+
* A markup string that cannot be parsed, with where it went wrong.
|
|
52
|
+
*
|
|
53
|
+
* [LAW:types-are-the-program] A subclass rather than optional fields on
|
|
54
|
+
* `MarkupError`, because the parent is also what `MarkupRegistry.register`
|
|
55
|
+
* throws, and a registration has no source text to point into. Every error this
|
|
56
|
+
* class describes has a location, so none of its fields is optional and no
|
|
57
|
+
* caller has to ask whether one is present — `instanceof` is the only question.
|
|
58
|
+
*
|
|
59
|
+
* [LAW:one-source-of-truth] The constructor takes the facts the parser holds —
|
|
60
|
+
* the string, the offset of the offending tag, what was open — and derives
|
|
61
|
+
* `line`, `column` and the message from them, so the numbers a caller reads
|
|
62
|
+
* and the caret the message draws cannot disagree.
|
|
63
|
+
*/
|
|
64
|
+
export class MarkupSyntaxError extends MarkupError {
|
|
65
|
+
/** The problem alone, with no location, for callers building their own message. */
|
|
66
|
+
reason;
|
|
67
|
+
/** The whole markup string that failed, as the caller passed it. */
|
|
68
|
+
markup;
|
|
69
|
+
/** Index into `markup` of the tag the parser rejected. */
|
|
70
|
+
offset;
|
|
71
|
+
/** 1-based line of `offset`; lines are separated by `\n`. */
|
|
72
|
+
line;
|
|
73
|
+
/** 1-based column of `offset` within its line, in UTF-16 code units. */
|
|
74
|
+
column;
|
|
75
|
+
/**
|
|
76
|
+
* The opening tags still open at `offset`, outermost first, as written. For
|
|
77
|
+
* overlapping plugin tags, only plugin tags are named.
|
|
78
|
+
*/
|
|
79
|
+
openTags;
|
|
80
|
+
constructor(reason, markup, offset, openTags) {
|
|
81
|
+
const lineStart = markup.lastIndexOf("\n", offset - 1) + 1;
|
|
82
|
+
const lineEnd = markup.indexOf("\n", offset);
|
|
83
|
+
const text = markup.slice(lineStart, lineEnd === -1 ? markup.length : lineEnd);
|
|
84
|
+
const line = countNewlines(markup.slice(0, lineStart)) + 1;
|
|
85
|
+
const column = offset - lineStart + 1;
|
|
86
|
+
const open = openTags.length > 0 ? openTags.join(" ") : "none";
|
|
87
|
+
super(`${printable(reason)} (line ${line}, column ${column})\n` +
|
|
88
|
+
`${excerpt(text, offset - lineStart)}\n` +
|
|
89
|
+
`Open tags: ${printable(open)}`);
|
|
90
|
+
this.name = "MarkupSyntaxError";
|
|
91
|
+
this.reason = reason;
|
|
92
|
+
this.markup = markup;
|
|
93
|
+
this.offset = offset;
|
|
94
|
+
this.line = line;
|
|
95
|
+
this.column = column;
|
|
96
|
+
this.openTags = openTags;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function countNewlines(text) {
|
|
100
|
+
return text.split("\n").length - 1;
|
|
101
|
+
}
|
|
102
|
+
// Code points shown either side of the error before the excerpt is cut and
|
|
103
|
+
// marked with an ellipsis. Enough to recognise the surrounding tags on a line
|
|
104
|
+
// that is long, templated, or machine-assembled, without printing all of it.
|
|
105
|
+
const EXCERPT_CONTEXT = 30;
|
|
106
|
+
/**
|
|
107
|
+
* `text` with each C0 control and DEL shown as one space.
|
|
108
|
+
*
|
|
109
|
+
* [LAW:single-enforcer] Every part of the message that quotes the caller's
|
|
110
|
+
* markup passes through here: the reason and the open tags quote tag text, and
|
|
111
|
+
* the tag grammar admits control characters, so an `ESC ]` inside a tag would
|
|
112
|
+
* otherwise open an OSC sequence in the terminal printing the error. In the
|
|
113
|
+
* excerpt it also keeps the caret aligned, since a control occupies no cell.
|
|
114
|
+
* The fields keep the text as written; only the message is made printable.
|
|
115
|
+
*/
|
|
116
|
+
function printable(text) {
|
|
117
|
+
return text.replace(/[\x00-\x1f\x7f]/g, " ");
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Two lines: the source line around `index`, and a caret under it.
|
|
121
|
+
*
|
|
122
|
+
* The window is cut in code points, so a surrogate pair is never split, and the
|
|
123
|
+
* caret is placed by cell width, so a wide character before the error still
|
|
124
|
+
* leaves it under the right cell.
|
|
125
|
+
*/
|
|
126
|
+
function excerpt(text, index) {
|
|
127
|
+
const chars = Array.from(printable(text));
|
|
128
|
+
const at = Array.from(text.slice(0, index)).length;
|
|
129
|
+
const from = Math.max(0, at - EXCERPT_CONTEXT);
|
|
130
|
+
const to = Math.min(chars.length, at + EXCERPT_CONTEXT);
|
|
131
|
+
const head = from > 0 ? "…" : "";
|
|
132
|
+
const tail = to < chars.length ? "…" : "";
|
|
133
|
+
const before = head + chars.slice(from, at).join("");
|
|
134
|
+
const shown = before + chars.slice(at, to).join("") + tail;
|
|
135
|
+
return ` ${shown}\n ${" ".repeat(cellLen(before))}^`;
|
|
136
|
+
}
|
|
48
137
|
// --- escape ---
|
|
49
138
|
/**
|
|
50
139
|
* Escapes markup characters so they render as literal text.
|
|
@@ -106,6 +195,9 @@ function parseTags(markup) {
|
|
|
106
195
|
}
|
|
107
196
|
return tags;
|
|
108
197
|
}
|
|
198
|
+
function wholeString(markup) {
|
|
199
|
+
return { source: markup, base: 0, enclosing: [] };
|
|
200
|
+
}
|
|
109
201
|
/**
|
|
110
202
|
* Parses the built-in style dialect — `[bold red]text[/bold red]` — into a
|
|
111
203
|
* `RichText` with styled spans. Module-private on purpose: `renderMarkup` is
|
|
@@ -127,7 +219,7 @@ function parseTags(markup) {
|
|
|
127
219
|
* function names; an empty registry puts it in the data, where the one
|
|
128
220
|
* boundary admits both.
|
|
129
221
|
*/
|
|
130
|
-
function render(markup, baseStyle, options) {
|
|
222
|
+
function render(markup, origin, baseStyle, options) {
|
|
131
223
|
// Fast path: no brackets at all
|
|
132
224
|
if (!HAS_TAG_RE.test(markup)) {
|
|
133
225
|
let text = markup;
|
|
@@ -145,6 +237,10 @@ function render(markup, baseStyle, options) {
|
|
|
145
237
|
let plainText = "";
|
|
146
238
|
const spans = [];
|
|
147
239
|
const openStack = [];
|
|
240
|
+
const unparsable = (reason, tag) => new MarkupSyntaxError(reason, origin.source, origin.base + tag.start, [
|
|
241
|
+
...origin.enclosing,
|
|
242
|
+
...openStack.map((opened) => opened.tag),
|
|
243
|
+
]);
|
|
148
244
|
let lastEnd = 0;
|
|
149
245
|
for (const tag of tags) {
|
|
150
246
|
// Add text before this tag
|
|
@@ -154,7 +250,9 @@ function render(markup, baseStyle, options) {
|
|
|
154
250
|
if (tag.isImplicitClose) {
|
|
155
251
|
// [/] — close the most recent open tag
|
|
156
252
|
if (openStack.length === 0) {
|
|
157
|
-
|
|
253
|
+
// [/] never closes a plugin tag, so an enclosing plugin tag can be open
|
|
254
|
+
// here while no style tag is.
|
|
255
|
+
throw unparsable(`Closing tag ${tag.fullMatch} has no open style tag to close`, tag);
|
|
158
256
|
}
|
|
159
257
|
const opened = openStack.pop();
|
|
160
258
|
spans.push(new Span(opened.textStart, plainText.length, openTagStyle(opened)));
|
|
@@ -163,7 +261,7 @@ function render(markup, baseStyle, options) {
|
|
|
163
261
|
// [/style] — find and close matching open tag
|
|
164
262
|
const idx = findLastOpen(openStack, tag.styleName);
|
|
165
263
|
if (idx === -1) {
|
|
166
|
-
throw
|
|
264
|
+
throw unparsable(`Closing tag ${tag.fullMatch} doesn't match any open tag`, tag);
|
|
167
265
|
}
|
|
168
266
|
const opened = openStack[idx];
|
|
169
267
|
spans.push(new Span(opened.textStart, plainText.length, openTagStyle(opened)));
|
|
@@ -172,6 +270,7 @@ function render(markup, baseStyle, options) {
|
|
|
172
270
|
else {
|
|
173
271
|
// Opening tag
|
|
174
272
|
openStack.push({
|
|
273
|
+
tag: tag.fullMatch,
|
|
175
274
|
styleName: tag.styleName,
|
|
176
275
|
parameters: tag.parameters,
|
|
177
276
|
textStart: plainText.length,
|
|
@@ -390,20 +489,23 @@ function parsePluginAttrs(after) {
|
|
|
390
489
|
* append path is uniform.
|
|
391
490
|
*/
|
|
392
491
|
export function renderMarkup(markup, options) {
|
|
492
|
+
return renderSlice(markup, wholeString(markup), options);
|
|
493
|
+
}
|
|
494
|
+
function renderSlice(markup, origin, options) {
|
|
393
495
|
const registry = options?.registry ?? globalMarkupRegistry;
|
|
394
496
|
const baseStyle = options?.baseStyle;
|
|
395
497
|
const doEmoji = options?.emoji !== false;
|
|
396
498
|
// Fast path: no `[` at all → no possible tags.
|
|
397
499
|
if (!HAS_TAG_RE.test(markup)) {
|
|
398
|
-
return render(markup, baseStyle, { emoji: doEmoji });
|
|
500
|
+
return render(markup, origin, baseStyle, { emoji: doEmoji });
|
|
399
501
|
}
|
|
400
502
|
const tags = parseTags(markup);
|
|
401
503
|
// Pair each opening plugin tag with its matching closer up-front, so the
|
|
402
504
|
// splice walk can just iterate top-level pairs in source order with no
|
|
403
505
|
// nested-state book-keeping.
|
|
404
|
-
const { annotated, topLevel: tagPairs } = pairPluginTags(tags, registry);
|
|
506
|
+
const { annotated, topLevel: tagPairs } = pairPluginTags(tags, registry, origin);
|
|
405
507
|
if (tagPairs.size === 0) {
|
|
406
|
-
return render(markup, baseStyle, { emoji: doEmoji });
|
|
508
|
+
return render(markup, origin, baseStyle, { emoji: doEmoji });
|
|
407
509
|
}
|
|
408
510
|
// [LAW:one-type-per-behavior] Always assemble a single RichText. Fragments
|
|
409
511
|
// between plugin pairs are parsed by the built-in `render`, plugin pairs
|
|
@@ -415,22 +517,26 @@ export function renderMarkup(markup, options) {
|
|
|
415
517
|
const open = annotated[openIdx];
|
|
416
518
|
const close = annotated[closeIdx];
|
|
417
519
|
if (open.start > cursor) {
|
|
418
|
-
out.append(render(markup.slice(cursor, open.start), baseStyle, { emoji: doEmoji }));
|
|
520
|
+
out.append(render(markup.slice(cursor, open.start), offsetBy(origin, cursor), baseStyle, { emoji: doEmoji }));
|
|
419
521
|
}
|
|
420
522
|
const innerRaw = markup.slice(open.end, close.start);
|
|
421
|
-
const innerRichText =
|
|
523
|
+
const innerRichText = renderSlice(innerRaw, { ...offsetBy(origin, open.end), enclosing: [...origin.enclosing, open.fullMatch] }, options);
|
|
422
524
|
const handler = registry.get(open.pluginName);
|
|
423
525
|
out.append(handler({ attrs: open.attrs, children: innerRichText, raw: innerRaw }));
|
|
424
526
|
cursor = close.end;
|
|
425
527
|
}
|
|
426
528
|
if (cursor < markup.length) {
|
|
427
|
-
out.append(render(markup.slice(cursor), baseStyle, { emoji: doEmoji }));
|
|
529
|
+
out.append(render(markup.slice(cursor), offsetBy(origin, cursor), baseStyle, { emoji: doEmoji }));
|
|
428
530
|
}
|
|
429
531
|
if (baseStyle)
|
|
430
532
|
out.stylize(baseStyle);
|
|
431
533
|
return out;
|
|
432
534
|
}
|
|
433
|
-
|
|
535
|
+
/** The origin of a sub-slice starting `offset` into the slice `origin` describes. */
|
|
536
|
+
function offsetBy(origin, offset) {
|
|
537
|
+
return { ...origin, base: origin.base + offset };
|
|
538
|
+
}
|
|
539
|
+
function pairPluginTags(tags, registry, origin) {
|
|
434
540
|
// Annotate tags with plugin info, then pair openers with closers. Only
|
|
435
541
|
// top-level pairs are returned; inner pairs will be re-discovered by the
|
|
436
542
|
// recursive `renderMarkup` call on the inner slice.
|
|
@@ -477,11 +583,18 @@ function pairPluginTags(tags, registry) {
|
|
|
477
583
|
// hand it. A style span may overlap because it annotates; a plugin pair
|
|
478
584
|
// may not because it replaces.
|
|
479
585
|
if (annotated[closeIdx].end > outerEnd) {
|
|
480
|
-
const inner = annotated[openIdx]
|
|
481
|
-
const outer = annotated[outerOpenIdx]
|
|
482
|
-
|
|
586
|
+
const inner = annotated[openIdx];
|
|
587
|
+
const outer = annotated[outerOpenIdx];
|
|
588
|
+
// The caret goes under the outer pair's closing tag, the point where
|
|
589
|
+
// nesting breaks. This pass sees plugin tags only; of those, a pair is
|
|
590
|
+
// open there when it opens before that tag and closes at or after it.
|
|
591
|
+
const caret = annotated[pairs.get(outerOpenIdx)].start;
|
|
592
|
+
const openAtCaret = sortedOpens
|
|
593
|
+
.filter((o) => annotated[o].start < caret && annotated[pairs.get(o)].start >= caret)
|
|
594
|
+
.map((o) => annotated[o].fullMatch);
|
|
595
|
+
throw new MarkupSyntaxError(`Plugin tag [${inner.pluginName}] overlaps [${outer.pluginName}]: plugin tags must nest, ` +
|
|
483
596
|
`because a handler receives one contiguous slice. ` +
|
|
484
|
-
`Close [/${inner}] before [/${outer}]
|
|
597
|
+
`Close [/${inner.pluginName}] before [/${outer.pluginName}].`, origin.source, origin.base + caret, [...origin.enclosing, ...openAtCaret]);
|
|
485
598
|
}
|
|
486
599
|
continue;
|
|
487
600
|
}
|
|
@@ -504,4 +617,3 @@ function annotatePluginTag(tag, registry) {
|
|
|
504
617
|
const attrs = tag.isClosing ? {} : parsePluginAttrs(after);
|
|
505
618
|
return { ...tag, pluginName: name, attrs };
|
|
506
619
|
}
|
|
507
|
-
//# sourceMappingURL=markup.js.map
|
package/dist/core/measure.d.ts
CHANGED
package/dist/core/measure.js
CHANGED
package/dist/core/oklch.d.ts
CHANGED
|
@@ -41,6 +41,13 @@ export interface ThemeKey {
|
|
|
41
41
|
/** Additive on lightness; applied *after* the scale. */
|
|
42
42
|
readonly lightnessShift: number;
|
|
43
43
|
}
|
|
44
|
+
/** How far `Oklch.mixAxes` moves each axis toward its target, each in [0, 1]. */
|
|
45
|
+
export interface OklchWeights {
|
|
46
|
+
readonly l: number;
|
|
47
|
+
readonly c: number;
|
|
48
|
+
readonly h: number;
|
|
49
|
+
readonly alpha: number;
|
|
50
|
+
}
|
|
44
51
|
export declare const IDENTITY: ThemeKey;
|
|
45
52
|
/**
|
|
46
53
|
* Flip lightness around the midpoint (`L' = 1 - L`) with hue and chroma
|
|
@@ -70,6 +77,7 @@ export declare function isIdentityKey(k: ThemeKey): boolean;
|
|
|
70
77
|
* sRGB-quantization step normalizes them.
|
|
71
78
|
*/
|
|
72
79
|
export declare class Oklch {
|
|
80
|
+
#private;
|
|
73
81
|
readonly l: number;
|
|
74
82
|
readonly c: number;
|
|
75
83
|
readonly h: number;
|
|
@@ -115,6 +123,32 @@ export declare class Oklch {
|
|
|
115
123
|
* there too.
|
|
116
124
|
*/
|
|
117
125
|
mix(toward: Oklch, t: number): Oklch;
|
|
126
|
+
/**
|
|
127
|
+
* `mix` with each axis moved its own share of the way: `weights.l` of the
|
|
128
|
+
* lightness gap, `weights.c` of the chroma gap, and so on, each in [0, 1].
|
|
129
|
+
* `mix(toward, t)` is `mixAxes` with every weight `t` — one interpolation,
|
|
130
|
+
* so the shorter-arc hue and the powerless-endpoint rule are the same
|
|
131
|
+
* whichever is called.
|
|
132
|
+
*
|
|
133
|
+
* It exists because perceptual axes are independent: a tint can take most of
|
|
134
|
+
* a hue's colourfulness while keeping close to the lightness it started at,
|
|
135
|
+
* which a single `t` cannot say — raising `t` for chroma drags lightness with
|
|
136
|
+
* it. Every weight is required: an axis left out would need a default, and
|
|
137
|
+
* "unchanged" (0) and "same as the others" are both plausible readings.
|
|
138
|
+
*
|
|
139
|
+
* A weak `h` beside a strong `c` shows the starting colour's hue at high
|
|
140
|
+
* chroma. The powerless rule covers only a truly achromatic start, so a
|
|
141
|
+
* near-grey whose hue is noise (a theme surface at c ≈ 0.004) keeps that
|
|
142
|
+
* noise in proportion. To land on the target's hue, pass `h: 1`.
|
|
143
|
+
*/
|
|
144
|
+
mixAxes(toward: Oklch, weights: OklchWeights): Oklch;
|
|
145
|
+
/**
|
|
146
|
+
* ΔE_OK — the Euclidean distance between this colour and `other` in OKLab
|
|
147
|
+
* (CSS Color 4's `deltaEOK`), where ~0.02 is the smallest difference the eye
|
|
148
|
+
* resolves. Alpha is not a coordinate of the space and does not count.
|
|
149
|
+
* Symmetric and pure.
|
|
150
|
+
*/
|
|
151
|
+
deltaE(other: Oklch): number;
|
|
118
152
|
/** Linear-sRGB coordinates for an explicit (l, C, h). Pure; `toRgba` passes
|
|
119
153
|
* already-normalized values so this never sees out-of-range inputs. */
|
|
120
154
|
private toLinearRgb;
|
|
@@ -125,4 +159,3 @@ export declare class Oklch {
|
|
|
125
159
|
*/
|
|
126
160
|
private findInGamutChroma;
|
|
127
161
|
}
|
|
128
|
-
//# sourceMappingURL=oklch.d.ts.map
|
package/dist/core/oklch.js
CHANGED
|
@@ -20,7 +20,11 @@
|
|
|
20
20
|
* nothing else from inside the project. Nothing in core/ depends back on
|
|
21
21
|
* this file.
|
|
22
22
|
*/
|
|
23
|
+
var _a;
|
|
23
24
|
import { ColorRgba } from "./color.js";
|
|
25
|
+
// The axes `mixAxes` checks, named once so a weights object's own keys — a
|
|
26
|
+
// missing one, or an extra field riding along — never decide what is checked.
|
|
27
|
+
const OKLCH_AXES = ["l", "c", "h", "alpha"];
|
|
24
28
|
export const IDENTITY = Object.freeze({
|
|
25
29
|
hueShift: 0,
|
|
26
30
|
chromaScale: 1,
|
|
@@ -144,7 +148,7 @@ export class Oklch {
|
|
|
144
148
|
const bLab = 0.0259040371 * lCube + 0.7827717662 * mCube - 0.8086757660 * sCube;
|
|
145
149
|
const C = Math.sqrt(aLab * aLab + bLab * bLab);
|
|
146
150
|
const H = wrapHue(Math.atan2(bLab, aLab) * (180 / Math.PI));
|
|
147
|
-
return new
|
|
151
|
+
return new _a(L, C, hueOf(C, H), a);
|
|
148
152
|
}
|
|
149
153
|
/**
|
|
150
154
|
* Polar → OKLab → linear → sRGB (with chroma-bisection gamut clamping).
|
|
@@ -181,7 +185,7 @@ export class Oklch {
|
|
|
181
185
|
const newL = clamp01(this.l * k.lightnessScale + k.lightnessShift);
|
|
182
186
|
const newC = Math.max(0, this.c * k.chromaScale);
|
|
183
187
|
const newH = wrapHue(this.h + k.hueShift);
|
|
184
|
-
return new
|
|
188
|
+
return new _a(newL, newC, hueOf(newC, newH), this.alpha);
|
|
185
189
|
}
|
|
186
190
|
/**
|
|
187
191
|
* The color `t` of the way from this one toward `toward`, in OKLCH.
|
|
@@ -208,6 +212,38 @@ export class Oklch {
|
|
|
208
212
|
if (!(t >= 0 && t <= 1)) {
|
|
209
213
|
throw new RangeError(`Oklch.mix: t must be in [0, 1]; got ${t}`);
|
|
210
214
|
}
|
|
215
|
+
return this.#interpolate(toward, t, t, t, t);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* `mix` with each axis moved its own share of the way: `weights.l` of the
|
|
219
|
+
* lightness gap, `weights.c` of the chroma gap, and so on, each in [0, 1].
|
|
220
|
+
* `mix(toward, t)` is `mixAxes` with every weight `t` — one interpolation,
|
|
221
|
+
* so the shorter-arc hue and the powerless-endpoint rule are the same
|
|
222
|
+
* whichever is called.
|
|
223
|
+
*
|
|
224
|
+
* It exists because perceptual axes are independent: a tint can take most of
|
|
225
|
+
* a hue's colourfulness while keeping close to the lightness it started at,
|
|
226
|
+
* which a single `t` cannot say — raising `t` for chroma drags lightness with
|
|
227
|
+
* it. Every weight is required: an axis left out would need a default, and
|
|
228
|
+
* "unchanged" (0) and "same as the others" are both plausible readings.
|
|
229
|
+
*
|
|
230
|
+
* A weak `h` beside a strong `c` shows the starting colour's hue at high
|
|
231
|
+
* chroma. The powerless rule covers only a truly achromatic start, so a
|
|
232
|
+
* near-grey whose hue is noise (a theme surface at c ≈ 0.004) keeps that
|
|
233
|
+
* noise in proportion. To land on the target's hue, pass `h: 1`.
|
|
234
|
+
*/
|
|
235
|
+
mixAxes(toward, weights) {
|
|
236
|
+
for (const axis of OKLCH_AXES) {
|
|
237
|
+
const w = weights[axis];
|
|
238
|
+
if (!(w >= 0 && w <= 1)) {
|
|
239
|
+
throw new RangeError(`Oklch.mixAxes: ${axis} weight must be in [0, 1]; got ${w}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
return this.#interpolate(toward, weights.l, weights.c, weights.h, weights.alpha);
|
|
243
|
+
}
|
|
244
|
+
// The one interpolation both entry points share; each has already checked
|
|
245
|
+
// its own weights, so this is the arithmetic alone.
|
|
246
|
+
#interpolate(toward, wl, wc, wh, walpha) {
|
|
211
247
|
const thisHasHue = this.c >= ACHROMATIC_EPS;
|
|
212
248
|
const towardHasHue = toward.c >= ACHROMATIC_EPS;
|
|
213
249
|
const fromH = wrapHue(thisHasHue ? this.h : towardHasHue ? toward.h : 0);
|
|
@@ -219,8 +255,19 @@ export class Oklch {
|
|
|
219
255
|
const arc = toH - fromH;
|
|
220
256
|
const fromU = arc < -180 ? fromH - 360 : fromH;
|
|
221
257
|
const toU = arc > 180 ? toH - 360 : toH;
|
|
222
|
-
const c = lerp(this.c, toward.c,
|
|
223
|
-
return new
|
|
258
|
+
const c = lerp(this.c, toward.c, wc);
|
|
259
|
+
return new _a(lerp(this.l, toward.l, wl), c, hueOf(c, wrapHue(lerp(fromU, toU, wh))), lerp(this.alpha, toward.alpha, walpha));
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* ΔE_OK — the Euclidean distance between this colour and `other` in OKLab
|
|
263
|
+
* (CSS Color 4's `deltaEOK`), where ~0.02 is the smallest difference the eye
|
|
264
|
+
* resolves. Alpha is not a coordinate of the space and does not count.
|
|
265
|
+
* Symmetric and pure.
|
|
266
|
+
*/
|
|
267
|
+
deltaE(other) {
|
|
268
|
+
const a = (this.h * Math.PI) / 180;
|
|
269
|
+
const b = (other.h * Math.PI) / 180;
|
|
270
|
+
return Math.hypot(this.l - other.l, this.c * Math.cos(a) - other.c * Math.cos(b), this.c * Math.sin(a) - other.c * Math.sin(b));
|
|
224
271
|
}
|
|
225
272
|
/** Linear-sRGB coordinates for an explicit (l, C, h). Pure; `toRgba` passes
|
|
226
273
|
* already-normalized values so this never sees out-of-range inputs. */
|
|
@@ -262,4 +309,4 @@ export class Oklch {
|
|
|
262
309
|
return lo;
|
|
263
310
|
}
|
|
264
311
|
}
|
|
265
|
-
|
|
312
|
+
_a = Oklch;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OSC 8 hyperlink wire grammar — which bytes may not appear inside a
|
|
3
|
+
* link, how a link becomes bytes, and how bytes are read back as a link.
|
|
4
|
+
*
|
|
5
|
+
* [LAW:one-source-of-truth] Every producer (`segmentsToString`,
|
|
6
|
+
* `Style.render`) opens a link through `osc8Open` and closes it with
|
|
7
|
+
* `OSC8_CLOSE`; every consumer that reads rendered bytes (a width measure, a
|
|
8
|
+
* test extracting URLs) matches them with `OSC8`; the data-model boundary
|
|
9
|
+
* (RichText) cleans URLs with `stripOscTerminators`. All four read the one
|
|
10
|
+
* terminator set below, so the bytes a sanitizer removes and the bytes a
|
|
11
|
+
* reader stops at cannot disagree.
|
|
12
|
+
*
|
|
13
|
+
* [LAW:locality-or-seam] This module depends on nothing; every consumer
|
|
14
|
+
* imports downward.
|
|
15
|
+
*
|
|
16
|
+
* The `id=` parameter is what makes one link hover as one link. A terminal
|
|
17
|
+
* treats cells as the same hyperlink when they share BOTH the URI and the id
|
|
18
|
+
* (the OSC 8 spec, and VTE / iTerm2 / kitty / WezTerm alike); without an id,
|
|
19
|
+
* each open sequence is its own link. The coalescer can only share one OSC 8
|
|
20
|
+
* pair across a run of identical SGR, so a link whose text changes style
|
|
21
|
+
* mid-span — a bold glyph beside plain text, a padded cell — is emitted as
|
|
22
|
+
* several pairs, and would highlight piecewise on hover.
|
|
23
|
+
*
|
|
24
|
+
* [LAW:types-are-the-program] The id is a pure function of the URI, so the
|
|
25
|
+
* byte stream stays a pure function of (style, text, colorSystem): no counter,
|
|
26
|
+
* no construction-order dependence. The consequence is deliberate: two spans
|
|
27
|
+
* with the same URI hover as one link wherever they sit on screen, adjacent
|
|
28
|
+
* or not — a click on either does the same thing, so they ARE one link.
|
|
29
|
+
* Because terminals key on the (id, URI) PAIR, a hash collision between two
|
|
30
|
+
* different URIs merges nothing — the URIs still differ — so a 32-bit hash is
|
|
31
|
+
* exact, not approximate.
|
|
32
|
+
*/
|
|
33
|
+
/** Remove every byte that could break a URL out of its OSC 8 wrap. */
|
|
34
|
+
export declare function stripOscTerminators(url: string): string;
|
|
35
|
+
/**
|
|
36
|
+
* The bytes that open a hyperlink to `url`.
|
|
37
|
+
*
|
|
38
|
+
* [LAW:single-enforcer] Wire-byte trust boundary — the URL is sanitized at
|
|
39
|
+
* the one place it becomes an OSC 8 sequence, whichever upstream API attached
|
|
40
|
+
* it to the Style (RichText sanitizes at its data-model boundary too; a Style
|
|
41
|
+
* built directly with `new Style({ link })` reaches here unsanitized).
|
|
42
|
+
*/
|
|
43
|
+
export declare function osc8Open(url: string): string;
|
|
44
|
+
/** The bytes that close the current hyperlink. */
|
|
45
|
+
export declare const OSC8_CLOSE = "\u001B]8;;\u001B\\";
|
|
46
|
+
/**
|
|
47
|
+
* Matches one OSC 8 sequence — an open or a close — terminated by `ESC \`,
|
|
48
|
+
* BEL, or the 8-bit ST. Group 1 is the params, group 2 the URI; both are empty
|
|
49
|
+
* on a close. To read bytes back use `osc8Sequences`; this pattern is exported
|
|
50
|
+
* for composing into a larger one (a width measure's zero-width alternation).
|
|
51
|
+
*/
|
|
52
|
+
export declare const OSC8: RegExp;
|
|
53
|
+
/** One OSC 8 sequence found in rendered bytes. A close has `uri === ""`. */
|
|
54
|
+
export interface Osc8Sequence {
|
|
55
|
+
/** Offset of the sequence in the scanned string. */
|
|
56
|
+
readonly index: number;
|
|
57
|
+
/** Length of the whole sequence, terminator included. */
|
|
58
|
+
readonly length: number;
|
|
59
|
+
/** The params field (`id=…`); empty on a close. */
|
|
60
|
+
readonly params: string;
|
|
61
|
+
/** The URI; empty on a close. */
|
|
62
|
+
readonly uri: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Every OSC 8 sequence in `text`, in order — the typed way to read rendered
|
|
66
|
+
* bytes back.
|
|
67
|
+
*/
|
|
68
|
+
export declare function osc8Sequences(text: string): Osc8Sequence[];
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The OSC 8 hyperlink wire grammar — which bytes may not appear inside a
|
|
3
|
+
* link, how a link becomes bytes, and how bytes are read back as a link.
|
|
4
|
+
*
|
|
5
|
+
* [LAW:one-source-of-truth] Every producer (`segmentsToString`,
|
|
6
|
+
* `Style.render`) opens a link through `osc8Open` and closes it with
|
|
7
|
+
* `OSC8_CLOSE`; every consumer that reads rendered bytes (a width measure, a
|
|
8
|
+
* test extracting URLs) matches them with `OSC8`; the data-model boundary
|
|
9
|
+
* (RichText) cleans URLs with `stripOscTerminators`. All four read the one
|
|
10
|
+
* terminator set below, so the bytes a sanitizer removes and the bytes a
|
|
11
|
+
* reader stops at cannot disagree.
|
|
12
|
+
*
|
|
13
|
+
* [LAW:locality-or-seam] This module depends on nothing; every consumer
|
|
14
|
+
* imports downward.
|
|
15
|
+
*
|
|
16
|
+
* The `id=` parameter is what makes one link hover as one link. A terminal
|
|
17
|
+
* treats cells as the same hyperlink when they share BOTH the URI and the id
|
|
18
|
+
* (the OSC 8 spec, and VTE / iTerm2 / kitty / WezTerm alike); without an id,
|
|
19
|
+
* each open sequence is its own link. The coalescer can only share one OSC 8
|
|
20
|
+
* pair across a run of identical SGR, so a link whose text changes style
|
|
21
|
+
* mid-span — a bold glyph beside plain text, a padded cell — is emitted as
|
|
22
|
+
* several pairs, and would highlight piecewise on hover.
|
|
23
|
+
*
|
|
24
|
+
* [LAW:types-are-the-program] The id is a pure function of the URI, so the
|
|
25
|
+
* byte stream stays a pure function of (style, text, colorSystem): no counter,
|
|
26
|
+
* no construction-order dependence. The consequence is deliberate: two spans
|
|
27
|
+
* with the same URI hover as one link wherever they sit on screen, adjacent
|
|
28
|
+
* or not — a click on either does the same thing, so they ARE one link.
|
|
29
|
+
* Because terminals key on the (id, URI) PAIR, a hash collision between two
|
|
30
|
+
* different URIs merges nothing — the URIs still differ — so a 32-bit hash is
|
|
31
|
+
* exact, not approximate.
|
|
32
|
+
*/
|
|
33
|
+
/**
|
|
34
|
+
* The bytes that prematurely terminate an OSC 8 sequence: ESC (`\x1b`, which
|
|
35
|
+
* begins ST `ESC \`), BEL (`\x07`), and the 8-bit ST (`\x9c`).
|
|
36
|
+
*/
|
|
37
|
+
const TERMINATORS = "\\x1b\\x07\\x9c";
|
|
38
|
+
const TERMINATOR_RE = new RegExp(`[${TERMINATORS}]`, "g");
|
|
39
|
+
/** Remove every byte that could break a URL out of its OSC 8 wrap. */
|
|
40
|
+
export function stripOscTerminators(url) {
|
|
41
|
+
return url.replace(TERMINATOR_RE, "");
|
|
42
|
+
}
|
|
43
|
+
const utf8 = new TextEncoder();
|
|
44
|
+
/**
|
|
45
|
+
* FNV-1a, 32-bit, over the URL's UTF-8 bytes, as 8 lowercase hex digits — a
|
|
46
|
+
* legal `id=` value (no `:`/`;`). UTF-8 rather than UTF-16 code units, so any
|
|
47
|
+
* other runtime computing standard FNV-1a over the same URL gets the same id.
|
|
48
|
+
*/
|
|
49
|
+
function linkId(url) {
|
|
50
|
+
let h = 0x811c9dc5;
|
|
51
|
+
for (const byte of utf8.encode(url)) {
|
|
52
|
+
h ^= byte;
|
|
53
|
+
h = Math.imul(h, 0x01000193);
|
|
54
|
+
}
|
|
55
|
+
return (h >>> 0).toString(16).padStart(8, "0");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The bytes that open a hyperlink to `url`.
|
|
59
|
+
*
|
|
60
|
+
* [LAW:single-enforcer] Wire-byte trust boundary — the URL is sanitized at
|
|
61
|
+
* the one place it becomes an OSC 8 sequence, whichever upstream API attached
|
|
62
|
+
* it to the Style (RichText sanitizes at its data-model boundary too; a Style
|
|
63
|
+
* built directly with `new Style({ link })` reaches here unsanitized).
|
|
64
|
+
*/
|
|
65
|
+
export function osc8Open(url) {
|
|
66
|
+
const clean = stripOscTerminators(url);
|
|
67
|
+
return `\x1b]8;id=${linkId(clean)};${clean}\x1b\\`;
|
|
68
|
+
}
|
|
69
|
+
/** The bytes that close the current hyperlink. */
|
|
70
|
+
export const OSC8_CLOSE = "\x1b]8;;\x1b\\";
|
|
71
|
+
/**
|
|
72
|
+
* Matches one OSC 8 sequence — an open or a close — terminated by `ESC \`,
|
|
73
|
+
* BEL, or the 8-bit ST. Group 1 is the params, group 2 the URI; both are empty
|
|
74
|
+
* on a close. To read bytes back use `osc8Sequences`; this pattern is exported
|
|
75
|
+
* for composing into a larger one (a width measure's zero-width alternation).
|
|
76
|
+
*/
|
|
77
|
+
export const OSC8 = new RegExp(`\\x1b\\]8;([^;${TERMINATORS}]*);([^${TERMINATORS}]*)(?:\\x1b\\\\|\\x07|\\x9c)`);
|
|
78
|
+
const OSC8_SCAN = new RegExp(OSC8.source, "g");
|
|
79
|
+
/**
|
|
80
|
+
* Every OSC 8 sequence in `text`, in order — the typed way to read rendered
|
|
81
|
+
* bytes back.
|
|
82
|
+
*/
|
|
83
|
+
export function osc8Sequences(text) {
|
|
84
|
+
return [...text.matchAll(OSC8_SCAN)].map((m) => ({
|
|
85
|
+
index: m.index,
|
|
86
|
+
length: m[0].length,
|
|
87
|
+
params: m[1],
|
|
88
|
+
uri: m[2],
|
|
89
|
+
}));
|
|
90
|
+
}
|
package/dist/core/pretty.d.ts
CHANGED
|
@@ -97,7 +97,27 @@ export declare class Pretty implements Renderable, Measurable {
|
|
|
97
97
|
private _format;
|
|
98
98
|
/** The arms for a non-null object, with `value` already on the open path. */
|
|
99
99
|
private _formatObject;
|
|
100
|
-
/**
|
|
100
|
+
/**
|
|
101
|
+
* One position, with every value in it laid out.
|
|
102
|
+
*
|
|
103
|
+
* `at.column` is where `slot.head` begins. Rather than hand-tracking a
|
|
104
|
+
* second, parallel "column so far" alongside `out` — two values a future
|
|
105
|
+
* edit could update out of step, silently reintroducing a stale-budget bug
|
|
106
|
+
* of the same shape this file just fixed — each hole derives its own
|
|
107
|
+
* column fresh from `out` via `lineColumn`, the one place "text just
|
|
108
|
+
* emitted" becomes "column now". [LAW:one-source-of-truth] `out` is
|
|
109
|
+
* already the complete record; `column` is a read of it, not a second copy
|
|
110
|
+
* of the same fact. `_joinOneLine`'s probe sibling tracks the analogous
|
|
111
|
+
* thing via `budget` shrinking instead, because a probe already discards
|
|
112
|
+
* anything that overruns and so never needs to know where a multi-line
|
|
113
|
+
* insert's last line ends.
|
|
114
|
+
*
|
|
115
|
+
* Each hole's `reserve` is its own `tail` plus, only for the slot's last
|
|
116
|
+
* hole, whatever `at.reserve` already asked this whole slot to leave room
|
|
117
|
+
* for (`_formatObject`'s trailing `,`). An earlier hole's tail is never
|
|
118
|
+
* folded into a later hole's reserve — it is spent, not carried, the moment
|
|
119
|
+
* `out` grows past it.
|
|
120
|
+
*/
|
|
101
121
|
private _expandSlot;
|
|
102
122
|
/** The one-line form of a value, or `null` when it will not fit `at.budget`. */
|
|
103
123
|
private _oneLine;
|
|
@@ -119,4 +139,3 @@ export declare class Pretty implements Renderable, Measurable {
|
|
|
119
139
|
private _slotOneLine;
|
|
120
140
|
private _addIndentGuides;
|
|
121
141
|
}
|
|
122
|
-
//# sourceMappingURL=pretty.d.ts.map
|