Skip to content

Commit 2d7c8c3

Browse files
add functionality to highlight blocks (#26)
1 parent c79cdc6 commit 2d7c8c3

File tree

12 files changed

+780
-25
lines changed

12 files changed

+780
-25
lines changed

typescript/README.md

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -239,16 +239,18 @@ MIT
239239

240240
- followed this for ts config setup: https://www.totaltypescript.com/tsconfig-cheat-sheet
241241

242-
243242
## Release workflow
244243

245-
We using bumpp to bump package.json and lock, create git tag and commit
246-
1. Run:
244+
We using bumpp to bump package.json and lock, create git tag and commit
245+
246+
1. Run:
247+
247248
```
248249
npx bumpp
249250
```
250251

251252
2. You should see:
253+
252254
```
253255
254256
4 Commits since the last version:
@@ -268,16 +270,14 @@ b7b30c1 : Make more typesafe
268270
from 0.2.0-alpha.3
269271
to 0.2.0-alpha.4
270272
```
273+
271274
You can choose a different version from the list or create new one. But bumpp is smart enough to use appropriate next version.
272275

273276
3. Verify and confirm
274277
4. Push commit and tag
275-
5. The new tag will trigger a release on github actions.
278+
5. The new tag will trigger a release on github actions.
276279
6. Go to github and create release using the new tag. Make sure you set the correct previous tag prefixed with `typescript-v`
277280

278-
279-
280-
281281
## Todos
282282

283283
- [x] setup eslint
@@ -295,6 +295,7 @@ You can choose a different version from the list or create new one. But bumpp is
295295
- [x] image
296296
- [x] blockquote
297297
- [x] fix ts and eslint errors in these dirs and remove from ignore list of ts and eslint
298+
298299
```
299300
"**/generated/**",
300301
"src/serialization/**",
@@ -306,4 +307,4 @@ You can choose a different version from the list or create new one. But bumpp is
306307
- [x] move vite app to typescript root examples dir
307308
- [] setup monorepo tooling
308309
- [] fix model generation for Image and RichText, then type renderers
309-
- [] use katex or similar package for equations
310+
- [] use katex or similar package for equations

typescript/examples/vite_basic/public/test_document.json

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,124 @@
2222
}
2323
},
2424
"children": [
25+
{
26+
"object": "block",
27+
"id": "bk_01jxj01879f8cvyq2hqc6p37z2",
28+
"type": "numbered_list_item",
29+
"created_time": "2025-06-12T11:57:32.236290Z",
30+
"created_by": {
31+
"object": "user",
32+
"id": "bcf6c03e-51a1-4f05-97d8-d616405b42a2"
33+
},
34+
"has_children": false,
35+
"metadata": {
36+
"origin": {
37+
"file_id": "file_01jxwgtg7qfr79j59j7xe777ek",
38+
"page_num": 1
39+
}
40+
},
41+
"numbered_list_item": {
42+
"rich_text": [
43+
{
44+
"type": "text",
45+
"text": {
46+
"content": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
47+
},
48+
"annotations": {},
49+
"plain_text": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
50+
}
51+
]
52+
}
53+
},
54+
55+
{
56+
"object": "block",
57+
"id": "bk_01jxj01879f8cvyq2hqc6p37z2",
58+
"type": "numbered_list_item",
59+
"created_time": "2025-06-12T11:57:32.236290Z",
60+
"created_by": {
61+
"object": "user",
62+
"id": "bcf6c03e-51a1-4f05-97d8-d616405b42a2"
63+
},
64+
"has_children": false,
65+
"metadata": {
66+
"origin": {
67+
"file_id": "file_01jxhzyhk6f2bvjjabjx4dxqje",
68+
"page_num": 1
69+
}
70+
},
71+
"numbered_list_item": {
72+
"rich_text": [
73+
{
74+
"type": "text",
75+
"text": {
76+
"content": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
77+
},
78+
"annotations": {},
79+
"plain_text": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
80+
}
81+
]
82+
}
83+
},
84+
{
85+
"object": "block",
86+
"id": "bk_01jxj01879f8cvyq2hqc6p37z2",
87+
"type": "numbered_list_item",
88+
"created_time": "2025-06-12T11:57:32.236290Z",
89+
"created_by": {
90+
"object": "user",
91+
"id": "bcf6c03e-51a1-4f05-97d8-d616405b42a2"
92+
},
93+
"has_children": false,
94+
"metadata": {
95+
"origin": {
96+
"file_id": "file_01jxhzyhk6f2bvjjabjx4dxqje",
97+
"page_num": 1
98+
}
99+
},
100+
"numbered_list_item": {
101+
"rich_text": [
102+
{
103+
"type": "text",
104+
"text": {
105+
"content": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
106+
},
107+
"annotations": {},
108+
"plain_text": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
109+
}
110+
]
111+
}
112+
},
113+
{
114+
"object": "block",
115+
"id": "bk_01jxj01879f8cvyq2hqc6p37z2",
116+
"type": "numbered_list_item",
117+
"created_time": "2025-06-12T11:57:32.236290Z",
118+
"created_by": {
119+
"object": "user",
120+
"id": "bcf6c03e-51a1-4f05-97d8-d616405b42a2"
121+
},
122+
"has_children": false,
123+
"metadata": {
124+
"origin": {
125+
"file_id": "file_01jxhzyhk6f2bvjjabjx4dxqje",
126+
"page_num": 1
127+
}
128+
},
129+
"numbered_list_item": {
130+
"rich_text": [
131+
{
132+
"type": "text",
133+
"text": {
134+
"content": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
135+
},
136+
"annotations": {},
137+
"plain_text": "Volume and premium OEMs, Tier-1 and Tier-n companies trying to \"shape the market\""
138+
}
139+
]
140+
}
141+
},
142+
25143
{
26144
"object": "block",
27145
"id": "bk_01jxwgvydvf8zts3qzst8nbkcq",

typescript/examples/vite_basic/src/App.css

Whitespace-only changes.

typescript/examples/vite_basic/src/App.tsx

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { useState, useEffect } from "react";
44

55
const App = () => {
66
const [testPage, setTestPage] = useState(null);
7+
const [devMode, setDevMode] = useState(false);
78

89
useEffect(() => {
910
const loadData = async () => {
@@ -22,6 +23,35 @@ const App = () => {
2223
return <div>Loading...</div>;
2324
}
2425

26+
// Test backrefs for highlighting
27+
const testBackrefs = [
28+
{
29+
end_idx: 50,
30+
block_id: "bk_01jxwgvye6er08spmyxj99f6cp",
31+
start_idx: 0,
32+
},
33+
{
34+
end_idx: 100,
35+
block_id: "bk_01jxwgvydyfj6rhm125q1rd4h8",
36+
start_idx: 70,
37+
},
38+
{
39+
end_idx: 80,
40+
block_id: "bk_01jxwgvydze3bsy2p19cfteqge",
41+
start_idx: 20,
42+
},
43+
// {
44+
// block_id: "bk_01jxwgvyehecbb2bv3jtnm9bzx",
45+
// start_idx: 0,
46+
// end_idx: 70,
47+
// },
48+
// {
49+
// block_id: "bk_01jxj01879f8cvyq2hqc6p37z2",
50+
// start_idx: 0,
51+
// end_idx: 170,
52+
// },
53+
];
54+
2555
return (
2656
<div
2757
style={{
@@ -37,10 +67,28 @@ const App = () => {
3767
>
3868
<div>
3969
<h1>JSON-DOC Renderer Development</h1>
70+
71+
<div style={{ marginBottom: "20px" }}>
72+
<button
73+
onClick={() => setDevMode(!devMode)}
74+
style={{
75+
padding: "8px 16px",
76+
background: devMode ? "oklch(60% 0.2 250)" : "oklch(40% 0.2 250)",
77+
color: "white",
78+
border: "none",
79+
borderRadius: "4px",
80+
cursor: "pointer",
81+
}}
82+
>
83+
{devMode ? "Disable" : "Enable"} Dev Mode
84+
</button>
85+
</div>
86+
4087
<JsonDocRenderer
4188
page={testPage}
4289
theme="dark"
43-
devMode={true}
90+
devMode={devMode}
91+
backrefs={testBackrefs}
4492
components={{
4593
page_delimiter: (props) => {
4694
return <PageDelimiter {...props} />;

typescript/src/renderer/JsonDocRenderer.tsx

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import "./styles/index.css";
2-
import React, { useEffect } from "react";
2+
import React from "react";
33

44
import { Page } from "@/models/generated";
55
// import { validateAgainstSchema } from "@/validation/validator";
@@ -8,6 +8,9 @@ import { BlockRenderer } from "./components/BlockRenderer";
88
import { PageDelimiter } from "./components/PageDelimiter";
99
import { JsonViewPanel } from "./components/dev/JsonViewPanel";
1010
import { RendererProvider } from "./context/RendererContext";
11+
import { HighlightNavigation } from "./components/HighlightNavigation";
12+
import { useHighlights } from "./hooks/useHighlights";
13+
import { Backref } from "./utils/highlightUtils";
1114

1215
interface JsonDocRendererProps {
1316
page: Page;
@@ -21,6 +24,7 @@ interface JsonDocRendererProps {
2124
resolveImageUrl?: (url: string) => Promise<string>;
2225
devMode?: boolean;
2326
viewJson?: boolean;
27+
backrefs?: Backref[];
2428
}
2529

2630
export const JsonDocRenderer = ({
@@ -31,23 +35,15 @@ export const JsonDocRenderer = ({
3135
resolveImageUrl,
3236
devMode = false,
3337
viewJson = false,
34-
// PageDelimiterComponent = PageDelimiter,
38+
backrefs = [],
3539
}: JsonDocRendererProps) => {
3640
console.log("page: ", page);
3741

38-
const loadAndValidate = async () => {
39-
// const response = await fetch("/schema/page/page_schema.json"); // Updated path
40-
// const data = await response.json();
41-
// console.log("schema: ", data);
42-
// validateAgainstSchema(
43-
// page,
44-
// )
45-
};
46-
47-
useEffect(() => {
48-
console.log("in jsondocrendererrrr");
49-
loadAndValidate();
50-
}, []);
42+
// Use the modular hooks for highlight management
43+
const { highlightCount, currentActiveIndex, navigateToHighlight } =
44+
useHighlights({
45+
backrefs,
46+
});
5147

5248
// return null;
5349
const renderedContent = (
@@ -113,6 +109,14 @@ export const JsonDocRenderer = ({
113109
) : (
114110
renderedContent
115111
)}
112+
{/* Show highlight navigation when there are highlights */}
113+
{highlightCount > 0 && (
114+
<HighlightNavigation
115+
highlightCount={highlightCount}
116+
onNavigate={navigateToHighlight}
117+
currentIndex={currentActiveIndex}
118+
/>
119+
)}
116120
</div>
117121
</RendererProvider>
118122
);
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
import React from "react";
2+
import { useHighlightNavigation } from "../hooks/useHighlightNavigation";
3+
4+
interface HighlightNavigationProps {
5+
highlightCount: number;
6+
onNavigate: (index: number) => void;
7+
currentIndex?: number;
8+
}
9+
10+
export const HighlightNavigation: React.FC<HighlightNavigationProps> = ({
11+
highlightCount,
12+
onNavigate,
13+
currentIndex: externalCurrentIndex,
14+
}) => {
15+
const { currentIndex, navigatePrevious, navigateNext, hasHighlights } =
16+
useHighlightNavigation({
17+
highlightCount,
18+
onNavigate,
19+
externalCurrentIndex,
20+
});
21+
22+
if (!hasHighlights) {
23+
return null;
24+
}
25+
26+
return (
27+
<div className="json-doc-highlight-navigation">
28+
<div className="highlight-nav-content">
29+
<button
30+
className="highlight-nav-button"
31+
onClick={navigatePrevious}
32+
aria-label="Previous highlight"
33+
title="Previous highlight (↑/k)"
34+
>
35+
up
36+
</button>
37+
38+
<span className="highlight-nav-counter">
39+
{currentIndex >= 0 ? currentIndex + 1 : "-"} of {highlightCount}
40+
</span>
41+
42+
<button
43+
className="highlight-nav-button"
44+
onClick={navigateNext}
45+
aria-label="Next highlight"
46+
title="Next highlight (↓/j)"
47+
>
48+
down
49+
</button>
50+
</div>
51+
</div>
52+
);
53+
};

typescript/src/renderer/components/blocks/TableBlockRenderer.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export const TableBlockRenderer: React.FC<TableBlockRendererProps> = ({
4242
<tr
4343
key={child.id || index}
4444
className="notion-table-row"
45+
data-block-id={child.id}
4546
>
4647
{rowData?.cells?.map(
4748
(cell: any, cellIndex: number) => {

0 commit comments

Comments
 (0)