@tooee/renderers 0.1.4 → 0.1.6

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.
@@ -0,0 +1,7 @@
1
+ import { RowDocumentRenderable } from "./RowDocumentRenderable.js";
2
+ declare module "@opentui/react" {
3
+ interface OpenTUIComponents {
4
+ "row-document": typeof RowDocumentRenderable;
5
+ }
6
+ }
7
+ //# sourceMappingURL=row-document.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-document.d.ts","sourceRoot":"","sources":["../src/row-document.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAIlE,OAAO,QAAQ,gBAAgB,CAAC;IAC9B,UAAU,iBAAiB;QACzB,cAAc,EAAE,OAAO,qBAAqB,CAAA;KAC7C;CACF"}
@@ -0,0 +1,4 @@
1
+ import { extend } from "@opentui/react";
2
+ import { RowDocumentRenderable } from "./RowDocumentRenderable.js";
3
+ extend({ "row-document": RowDocumentRenderable });
4
+ //# sourceMappingURL=row-document.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"row-document.js","sourceRoot":"","sources":["../src/row-document.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAA;AACvC,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AAElE,MAAM,CAAC,EAAE,cAAc,EAAE,qBAAqB,EAAE,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tooee/renderers",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Content renderers (markdown, code, image, table) for Tooee",
5
5
  "license": "MIT",
6
6
  "author": "Gareth Andrew",
@@ -37,7 +37,7 @@
37
37
  "typecheck": "tsc --noEmit"
38
38
  },
39
39
  "dependencies": {
40
- "@tooee/themes": "0.1.4",
40
+ "@tooee/themes": "0.1.6",
41
41
  "marked": "^16.3.0"
42
42
  },
43
43
  "devDependencies": {
package/src/CodeView.tsx CHANGED
@@ -1,6 +1,7 @@
1
- import { useEffect, useRef } from "react"
2
- import type { LineNumberRenderable } from "@opentui/core"
1
+ import { useEffect, useRef, type RefObject } from "react"
3
2
  import { useTheme } from "@tooee/themes"
3
+ import type { RowDocumentRenderable, RowDocumentPalette, RowDocumentDecorations } from "./RowDocumentRenderable.js"
4
+ import "./row-document.js"
4
5
 
5
6
  interface CodeViewProps {
6
7
  content: string
@@ -12,6 +13,7 @@ interface CodeViewProps {
12
13
  matchingLines?: Set<number>
13
14
  currentMatchLine?: number
14
15
  toggledLines?: Set<number>
16
+ docRef?: RefObject<RowDocumentRenderable | null>
15
17
  }
16
18
 
17
19
  export function CodeView({
@@ -24,94 +26,50 @@ export function CodeView({
24
26
  matchingLines,
25
27
  currentMatchLine,
26
28
  toggledLines,
29
+ docRef,
27
30
  }: CodeViewProps) {
28
31
  const { syntax, theme } = useTheme()
29
- const lineNumRef = useRef<LineNumberRenderable>(null)
32
+ const internalRef = useRef<RowDocumentRenderable>(null)
33
+ const effectiveRef = docRef ?? internalRef
30
34
 
31
- useEffect(() => {
32
- const ref = lineNumRef.current
33
- if (!ref) return
34
-
35
- ref.clearAllLineColors()
36
- ref.clearAllLineSigns()
35
+ const palette: RowDocumentPalette = {
36
+ gutterFg: theme.textMuted,
37
+ gutterBg: theme.backgroundElement,
38
+ cursorBg: theme.cursorLine,
39
+ selectionBg: theme.selection,
40
+ matchBg: theme.warning,
41
+ currentMatchBg: theme.primary,
42
+ toggledBg: theme.backgroundPanel,
43
+ cursorSignFg: theme.primary,
44
+ matchSignFg: theme.warning,
45
+ currentMatchSignFg: theme.primary,
46
+ }
37
47
 
38
- // Search matches
39
- if (matchingLines) {
40
- for (const line of matchingLines) {
41
- ref.setLineSign(line, {
42
- after: "●",
43
- afterColor: line === currentMatchLine ? theme.primary : theme.warning,
44
- })
45
- }
46
- }
47
-
48
- // Selection range
49
- if (selectionStart != null && selectionEnd != null) {
50
- for (let i = selectionStart; i <= selectionEnd; i++) {
51
- ref.setLineColor(i, { content: theme.selection, gutter: theme.selection })
52
- }
53
- }
54
-
55
- if (toggledLines) {
56
- for (const line of toggledLines) {
57
- const isSelected =
58
- selectionStart != null && selectionEnd != null && line >= selectionStart && line <= selectionEnd
59
- if (line === cursor || isSelected) continue
60
- ref.setLineColor(line, {
61
- content: theme.backgroundPanel,
62
- gutter: theme.backgroundPanel,
63
- })
64
- }
65
- }
66
-
67
- // Cursor line (overwrites selection color on cursor line)
68
- if (cursor != null) {
69
- ref.setLineColor(cursor, { content: theme.cursorLine, gutter: theme.cursorLine })
70
- ref.setLineSign(cursor, {
71
- before: "▸",
72
- beforeColor: theme.primary,
73
- // Preserve search match sign if present
74
- ...(matchingLines?.has(cursor)
75
- ? {
76
- after: "●",
77
- afterColor: cursor === currentMatchLine ? theme.primary : theme.warning,
78
- }
79
- : {}),
80
- })
48
+ useEffect(() => {
49
+ const decorations: RowDocumentDecorations = {
50
+ cursorRow: cursor,
51
+ selection: selectionStart != null && selectionEnd != null
52
+ ? { start: selectionStart, end: selectionEnd }
53
+ : null,
54
+ matchingRows: matchingLines,
55
+ currentMatchRow: currentMatchLine,
56
+ toggledRows: toggledLines,
81
57
  }
82
- }, [
83
- content,
84
- cursor,
85
- selectionStart,
86
- selectionEnd,
87
- matchingLines,
88
- currentMatchLine,
89
- toggledLines,
90
- theme,
91
- ])
58
+ effectiveRef.current?.setDecorations(decorations)
59
+ }, [cursor, selectionStart, selectionEnd, matchingLines, currentMatchLine, toggledLines])
92
60
 
93
61
  const codeElement = <code content={content} filetype={language} syntaxStyle={syntax} />
94
62
 
95
63
  return (
96
- <box
97
- style={{
98
- flexDirection: "column",
99
- }}
64
+ <row-document
65
+ ref={effectiveRef}
66
+ key={theme.textMuted + theme.backgroundElement}
67
+ showLineNumbers={showLineNumbers}
68
+ palette={palette}
69
+ signColumnWidth={1}
70
+ style={{ flexGrow: 1 }}
100
71
  >
101
- {showLineNumbers ? (
102
- <line-number
103
- ref={lineNumRef}
104
- key={theme.textMuted + theme.backgroundElement}
105
- fg={theme.textMuted}
106
- bg={theme.backgroundElement}
107
- paddingRight={1}
108
- showLineNumbers
109
- >
110
- {codeElement}
111
- </line-number>
112
- ) : (
113
- codeElement
114
- )}
115
- </box>
72
+ {codeElement}
73
+ </row-document>
116
74
  )
117
75
  }
@@ -1,83 +1,78 @@
1
1
  import { marked, type Token, type Tokens } from "marked"
2
- import type { ReactNode } from "react"
2
+ import { useEffect, useRef, type ReactNode, type RefObject } from "react"
3
3
  import { useTheme, type ResolvedTheme } from "@tooee/themes"
4
4
  import type { SyntaxStyle } from "@opentui/core"
5
5
  import { Table } from "./Table.js"
6
+ import type { RowDocumentRenderable, RowDocumentPalette, RowDocumentDecorations } from "./RowDocumentRenderable.js"
7
+ import "./row-document.js"
6
8
 
7
9
  interface MarkdownViewProps {
8
10
  content: string
11
+ showLineNumbers?: boolean
9
12
  activeBlock?: number
10
13
  selectedBlocks?: { start: number; end: number }
11
14
  matchingBlocks?: Set<number>
12
15
  currentMatchBlock?: number
13
16
  toggledBlocks?: Set<number>
17
+ docRef?: RefObject<RowDocumentRenderable | null>
14
18
  }
15
19
 
16
20
  export function MarkdownView({
17
21
  content,
22
+ showLineNumbers = true,
18
23
  activeBlock,
19
24
  selectedBlocks,
20
25
  matchingBlocks,
21
26
  currentMatchBlock,
22
27
  toggledBlocks,
28
+ docRef,
23
29
  }: MarkdownViewProps) {
24
30
  const { theme, syntax } = useTheme()
31
+ const internalRef = useRef<RowDocumentRenderable>(null)
32
+ const effectiveRef = docRef ?? internalRef
25
33
  const tokens = marked.lexer(content)
26
34
  const blocks = tokens.filter((t) => t.type !== "space")
27
35
 
28
- return (
29
- <box style={{ flexDirection: "column" }}>
30
- {blocks.map((token, index) => {
31
- const { accent: accentColor, background: bgColor } = getBlockStyle(
32
- index,
33
- theme,
34
- activeBlock,
35
- selectedBlocks,
36
- matchingBlocks,
37
- currentMatchBlock,
38
- toggledBlocks,
39
- )
40
- const blockContent = (
41
- <TokenRenderer key={index} token={token} theme={theme} syntax={syntax} />
42
- )
36
+ const palette: RowDocumentPalette = {
37
+ gutterFg: theme.textMuted,
38
+ gutterBg: theme.backgroundElement,
39
+ cursorBg: theme.cursorLine,
40
+ selectionBg: theme.selection,
41
+ matchBg: theme.warning,
42
+ currentMatchBg: theme.primary,
43
+ toggledBg: theme.backgroundPanel,
44
+ cursorSignFg: theme.primary,
45
+ matchSignFg: theme.warning,
46
+ currentMatchSignFg: theme.primary,
47
+ }
43
48
 
44
- if (accentColor) {
45
- return (
46
- <box
47
- key={index}
48
- style={{ flexDirection: "row" }}
49
- backgroundColor={bgColor ?? undefined}
50
- >
51
- <text content="▎" fg={accentColor} />
52
- <box style={{ flexGrow: 1, flexDirection: "column" }}>{blockContent}</box>
53
- </box>
54
- )
55
- }
49
+ useEffect(() => {
50
+ const decorations: RowDocumentDecorations = {
51
+ cursorRow: activeBlock,
52
+ selection: selectedBlocks ? { start: selectedBlocks.start, end: selectedBlocks.end } : null,
53
+ matchingRows: matchingBlocks,
54
+ currentMatchRow: currentMatchBlock,
55
+ toggledRows: toggledBlocks,
56
+ }
57
+ effectiveRef.current?.setDecorations(decorations)
58
+ }, [activeBlock, selectedBlocks, matchingBlocks, currentMatchBlock, toggledBlocks])
56
59
 
57
- return <box key={index}>{blockContent}</box>
58
- })}
59
- </box>
60
- )
61
- }
60
+ const blockElements = blocks.map((token, index) => (
61
+ <TokenRenderer key={index} token={token} theme={theme} syntax={syntax} />
62
+ ))
62
63
 
63
- function getBlockStyle(
64
- index: number,
65
- theme: ResolvedTheme,
66
- activeBlock?: number,
67
- selectedBlocks?: { start: number; end: number },
68
- matchingBlocks?: Set<number>,
69
- currentMatchBlock?: number,
70
- toggledBlocks?: Set<number>,
71
- ): { accent: string | null; background: string | null } {
72
- // Priority: cursor > toggled > current match > match > selection
73
- if (activeBlock === index) return { accent: theme.primary, background: theme.backgroundElement }
74
- if (toggledBlocks?.has(index)) return { accent: theme.secondary, background: theme.backgroundPanel }
75
- if (currentMatchBlock === index) return { accent: theme.accent, background: null }
76
- if (matchingBlocks?.has(index)) return { accent: theme.warning, background: null }
77
- if (selectedBlocks && index >= selectedBlocks.start && index <= selectedBlocks.end) {
78
- return { accent: theme.secondary, background: theme.backgroundPanel }
79
- }
80
- return { accent: null, background: null }
64
+ return (
65
+ <row-document
66
+ ref={effectiveRef}
67
+ key={theme.textMuted + theme.backgroundElement}
68
+ showLineNumbers={showLineNumbers}
69
+ palette={palette}
70
+ signColumnWidth={1}
71
+ style={{ flexGrow: 1 }}
72
+ >
73
+ {blockElements}
74
+ </row-document>
75
+ )
81
76
  }
82
77
 
83
78
  function TokenRenderer({