Skip to content

Commit 44e34b0

Browse files
committed
feat: Create Neo.grid.Row component (#8965)
1 parent 331d58f commit 44e34b0

9 files changed

Lines changed: 742 additions & 5 deletions

File tree

resources/content/.sync-metadata.json

Lines changed: 45 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"lastSync": "2026-02-03T11:50:56.066Z",
3-
"releasesLastFetched": "2026-02-03T11:50:56.737Z",
2+
"lastSync": "2026-02-03T13:52:33.604Z",
3+
"releasesLastFetched": "2026-02-03T13:52:34.352Z",
44
"pushFailures": [],
55
"issues": {
66
"3789": {
@@ -18439,7 +18439,7 @@
1843918439
"path": "resources/content/issues/issue-8930.md",
1844018440
"closedAt": null,
1844118441
"updatedAt": "2026-02-02T23:03:32Z",
18442-
"contentHash": "b307b7fda13116da9272fee1b86d6e8867d400a59c564b47800b933926da30d1"
18442+
"contentHash": "27e6ea0e64ff373870c3dc809605f84c381dae4a5c34a73acc97073aa89e8115"
1844318443
},
1844418444
"8931": {
1844518445
"state": "CLOSED",
@@ -18636,6 +18636,48 @@
1863618636
"closedAt": "2026-02-03T10:40:33Z",
1863718637
"updatedAt": "2026-02-03T10:40:33Z",
1863818638
"contentHash": "4504dc3f190a50a62d09aec4d8070369f3adc4ee5bd19ed18997c6682387a8cc"
18639+
},
18640+
"8959": {
18641+
"state": "CLOSED",
18642+
"path": "resources/content/issues/issue-8959.md",
18643+
"closedAt": "2026-02-03T12:32:51Z",
18644+
"updatedAt": "2026-02-03T12:32:51Z",
18645+
"contentHash": "852b7e1cb41da53740c4ba62a7796a9c6421ed01f852112400a9436e91a29d0b"
18646+
},
18647+
"8960": {
18648+
"state": "CLOSED",
18649+
"path": "resources/content/issues/issue-8960.md",
18650+
"closedAt": "2026-02-03T12:43:49Z",
18651+
"updatedAt": "2026-02-03T12:43:50Z",
18652+
"contentHash": "f2c0b8cc11bee55db31d011054f872f0b10511b663b8e3aba32a996bad57f37e"
18653+
},
18654+
"8961": {
18655+
"state": "CLOSED",
18656+
"path": "resources/content/issues/issue-8961.md",
18657+
"closedAt": "2026-02-03T13:12:00Z",
18658+
"updatedAt": "2026-02-03T13:12:00Z",
18659+
"contentHash": "4188b7ece9cdbac52e5fbe0dd5997faf700ae57469bb3878d4809505fddfbb8f"
18660+
},
18661+
"8962": {
18662+
"state": "CLOSED",
18663+
"path": "resources/content/issues/issue-8962.md",
18664+
"closedAt": "2026-02-03T13:18:18Z",
18665+
"updatedAt": "2026-02-03T13:18:18Z",
18666+
"contentHash": "b2a53b99823365472ccc549e5e4ce992f8424bef91dee130cb0a5020eab5c29a"
18667+
},
18668+
"8963": {
18669+
"state": "CLOSED",
18670+
"path": "resources/content/issues/issue-8963.md",
18671+
"closedAt": "2026-02-03T13:29:55Z",
18672+
"updatedAt": "2026-02-03T13:29:55Z",
18673+
"contentHash": "acc914d4ccfb547c5b202109d411875095e88be206eff96ddef99249ecbb89a4"
18674+
},
18675+
"8964": {
18676+
"state": "OPEN",
18677+
"path": "resources/content/issues/issue-8964.md",
18678+
"closedAt": null,
18679+
"updatedAt": "2026-02-03T13:50:43Z",
18680+
"contentHash": "dbbfb39525661b230c20d3c54d2fb2ee60f6bb486aabed30ddcbf37ab8f7ad54"
1863918681
}
1864018682
},
1864118683
"releases": {

resources/content/issues/issue-8930.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,13 @@ subIssues:
3333
- '[x] 8947 Docs: Sparkline Knowledge Base Enhancement'
3434
- '[x] 8957 Fix: Sparkline Pulse Artifact Persistence'
3535
- '[x] 8958 Enhancement: Sparkline Scanner Text Positioning'
36-
subIssuesCompleted: 16
37-
subIssuesTotal: 19
36+
- '[x] 8959 Feat: Implement hideMode for Grid Columns to support OffscreenCanvas buffering'
37+
- '[x] 8960 Fix: Vertical scrolling causes OffscreenCanvas loss in Grid cells'
38+
- '[x] 8961 Fix: Memory Leak in Neo.component.Canvas due to missing destroy()'
39+
- '[x] 8962 Perf: Apply CSS containment to Grid Rows'
40+
- '[x] 8963 Feat: Smooth Data Transitions for Sparklines'
41+
subIssuesCompleted: 21
42+
subIssuesTotal: 24
3843
blockedBy: []
3944
blocking: []
4045
---
@@ -97,4 +102,9 @@ The goal is to create a meritocratic index of the open-source ecosystem, powered
97102
98103
- 2026-02-03T10:24:39Z @tobiu added sub-issue #8957
99104
- 2026-02-03T10:39:03Z @tobiu added sub-issue #8958
105+
- 2026-02-03T12:32:20Z @tobiu added sub-issue #8959
106+
- 2026-02-03T12:43:12Z @tobiu added sub-issue #8960
107+
- 2026-02-03T13:01:53Z @tobiu added sub-issue #8961
108+
- 2026-02-03T13:01:55Z @tobiu added sub-issue #8962
109+
- 2026-02-03T13:01:57Z @tobiu added sub-issue #8963
100110

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
---
2+
id: 8959
3+
title: 'Feat: Implement hideMode for Grid Columns to support OffscreenCanvas buffering'
4+
state: CLOSED
5+
labels:
6+
- enhancement
7+
- ai
8+
assignees:
9+
- tobiu
10+
createdAt: '2026-02-03T12:11:03Z'
11+
updatedAt: '2026-02-03T12:32:51Z'
12+
githubUrl: 'https://github.com/neomjs/neo/issues/8959'
13+
author: tobiu
14+
commentsCount: 1
15+
parentIssue: 8930
16+
subIssues: []
17+
subIssuesCompleted: 0
18+
subIssuesTotal: 0
19+
blockedBy: []
20+
blocking: []
21+
closedAt: '2026-02-03T12:32:51Z'
22+
---
23+
# Feat: Implement hideMode for Grid Columns to support OffscreenCanvas buffering
24+
25+
We need to implement a buffering strategy for Grid Columns to support cases like `OffscreenCanvas` where removing the DOM node destroys the worker connection context.
26+
27+
**Proposed Changes:**
28+
1. **`src/grid/column/Base.mjs`**:
29+
- Add `hideMode` config (String, default: `'removeDom'`).
30+
- Valid values: `'removeDom'`, `'visibility'` (uses `visibility: hidden`), `'display'` (uses `display: none`).
31+
- Note: `visibility` is preferred for Canvas to maintain layout geometry.
32+
33+
2. **`src/grid/Body.mjs`**:
34+
- Update rendering logic (`createRow`) to ensure columns with `hideMode !== 'removeDom'` are always rendered in the VDOM, regardless of whether they are within the `mountedColumns` window.
35+
- If such a column is outside the mounted range, it will be rendered with the appropriate hiding style.
36+
37+
**Benefits:**
38+
- Solves the "Zombie Canvas" issue where horizontally scrolling destroys `OffscreenCanvas` connections.
39+
- Improves scrolling performance for heavy column components by avoiding reconstruction.
40+
41+
42+
## Timeline
43+
44+
- 2026-02-03T12:11:04Z @tobiu added the `enhancement` label
45+
- 2026-02-03T12:11:04Z @tobiu added the `ai` label
46+
- 2026-02-03T12:32:09Z @tobiu referenced in commit `e15bb38` - "Feat: Implement hideMode for Grid Columns to support OffscreenCanvas buffering (#8959)"
47+
- 2026-02-03T12:32:20Z @tobiu added parent issue #8930
48+
### @tobiu - 2026-02-03T12:32:30Z
49+
50+
**Input from Gemini 3 Pro:**
51+
52+
> ✦ I have implemented the new `hideMode` configuration for Grid Columns and enabled it by default for `Sparkline` columns.
53+
>
54+
> **Changes Implemented:**
55+
>
56+
> 1. **`src/grid/column/Base.mjs`**:
57+
> - Added `hideMode` config (values: `'removeDom'`, `'visibility'`, `'display'`).
58+
> - Default: `'removeDom'` (preserves existing behavior for standard columns).
59+
>
60+
> 2. **`src/grid/Body.mjs`**:
61+
> - Modified `createRow` to render *all* columns during row creation, not just the mounted range.
62+
> - Columns outside the `mountedColumns` range are now checked:
63+
> - If `hideMode === 'removeDom'`, they are skipped (virtualized).
64+
> - If `hideMode === 'visibility'`, they are rendered with `visibility: hidden` (buffered).
65+
> - If `hideMode === 'display'`, they are rendered with `display: none` (buffered).
66+
>
67+
> 3. **`src/grid/column/Sparkline.mjs`**:
68+
> - Set default `hideMode: 'visibility'`.
69+
>
70+
> **Outcome:**
71+
> Horizontal scrolling no longer destroys the DOM nodes for Sparkline columns. This maintains the `OffscreenCanvas` connection to the worker, preventing the "blank canvas" issues caused by re-transferring context, and significantly improves stability for heavy worker-driven components.
72+
73+
- 2026-02-03T12:32:37Z @tobiu assigned to @tobiu
74+
- 2026-02-03T12:32:51Z @tobiu closed this issue
75+
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
---
2+
id: 8960
3+
title: 'Fix: Vertical scrolling causes OffscreenCanvas loss in Grid cells'
4+
state: CLOSED
5+
labels:
6+
- bug
7+
- ai
8+
assignees:
9+
- tobiu
10+
createdAt: '2026-02-03T12:36:23Z'
11+
updatedAt: '2026-02-03T12:43:50Z'
12+
githubUrl: 'https://github.com/neomjs/neo/issues/8960'
13+
author: tobiu
14+
commentsCount: 1
15+
parentIssue: 8930
16+
subIssues: []
17+
subIssuesCompleted: 0
18+
subIssuesTotal: 0
19+
blockedBy: []
20+
blocking: []
21+
closedAt: '2026-02-03T12:43:49Z'
22+
---
23+
# Fix: Vertical scrolling causes OffscreenCanvas loss in Grid cells
24+
25+
We are observing a glitch where vertically scrolling the grid can cause some OffscreenCanvas instances (like Sparklines) to be "lost" or appear blank.
26+
27+
**Analysis:**
28+
This likely relates to the buffered row range mechanism in `Neo.grid.Body`. When rows are recycled or moved, the VDOM diffing engine might be generating `move` operations or destructive updates that interfere with the `OffscreenCanvas` persistence, similar to the horizontal scrolling issue but on the vertical axis.
29+
30+
**Investigation Plan:**
31+
1. Inspect `src/grid/Body.mjs` and its row recycling/buffering logic.
32+
2. Analyze VDOM deltas during vertical scrolling to identify if DOM nodes are being destroyed/recreated instead of moved/updated.
33+
3. Ensure that `canvas` elements within the recycled rows maintain their worker association.
34+
35+
**Goal:**
36+
Ensure `OffscreenCanvas` instances remain stable and connected to their workers during vertical scrolling and row recycling.
37+
38+
39+
## Timeline
40+
41+
- 2026-02-03T12:36:24Z @tobiu added the `bug` label
42+
- 2026-02-03T12:36:24Z @tobiu added the `ai` label
43+
- 2026-02-03T12:43:01Z @tobiu referenced in commit `71ba8ff` - "Fix: Vertical scrolling causes OffscreenCanvas loss in Grid cells (#8960)"
44+
- 2026-02-03T12:43:12Z @tobiu added parent issue #8930
45+
### @tobiu - 2026-02-03T12:43:28Z
46+
47+
**Input from Gemini 3 Pro:**
48+
49+
> ✦ I have implemented the fix for the vertical scrolling glitch where OffscreenCanvas instances were being lost.
50+
>
51+
> **Root Cause:**
52+
> The `mountedRows` logic in `src/grid/Body.mjs` was calculating the render window based strictly on `visible + buffer`. At the boundaries of the dataset (top and bottom), this window size was smaller than the `Recycle Modulus` (available rows + 2 * buffer).
53+
> Because the number of rendered rows fluctuated (e.g., shrinking to 15 at the top vs 20 in the middle), the VDOM engine interpreted the re-appearance of the "spare" rows as **new insertions** (`insertNode`), which destroys the DOM node and the associated Worker connection.
54+
>
55+
> **Fix Implemented:**
56+
> Modified `updateMountedAndVisibleRows` to enforce a **Constant Window Size** equal to the Recycle Modulus.
57+
> - At the top of the grid, the window is "pushed down" to include look-ahead rows, filling the recycle pool.
58+
> - At the bottom, the window is "pulled up" to include look-behind rows.
59+
>
60+
> **Outcome:**
61+
> The set of Row IDs `{row-0 ... row-N}` now remains constant as long as the dataset is large enough. This ensures the VDOM engine always generates `moveNode` operations for row recycling, preserving the `OffscreenCanvas` contexts and eliminating the visual glitch during vertical scrolling.
62+
63+
- 2026-02-03T12:43:37Z @tobiu assigned to @tobiu
64+
- 2026-02-03T12:43:50Z @tobiu closed this issue
65+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
---
2+
id: 8961
3+
title: 'Fix: Memory Leak in Neo.component.Canvas due to missing destroy()'
4+
state: CLOSED
5+
labels:
6+
- bug
7+
- ai
8+
assignees:
9+
- tobiu
10+
createdAt: '2026-02-03T13:01:21Z'
11+
updatedAt: '2026-02-03T13:12:00Z'
12+
githubUrl: 'https://github.com/neomjs/neo/issues/8961'
13+
author: tobiu
14+
commentsCount: 1
15+
parentIssue: 8930
16+
subIssues: []
17+
subIssuesCompleted: 0
18+
subIssuesTotal: 0
19+
blockedBy: []
20+
blocking: []
21+
closedAt: '2026-02-03T13:12:00Z'
22+
---
23+
# Fix: Memory Leak in Neo.component.Canvas due to missing destroy()
24+
25+
`Neo.component.Canvas` is missing a `destroy()` method. While it correctly registers the canvas with the worker on mount, it fails to unregister it on destruction.
26+
27+
**Consequence:**
28+
The Canvas Worker retains `OffscreenCanvas` contexts, render loops, and data structures indefinitely, leading to a memory leak and potential GPU process crash over time as views are created and destroyed.
29+
30+
**Task:**
31+
1. Implement `destroy()` in `src/component/Canvas.mjs`.
32+
2. Send a `destroyCanvas` (or similar unregister) message to the Canvas Worker.
33+
3. Implement the corresponding `destroyCanvas` handler in `src/worker/Canvas.mjs` (and specifically in `src/canvas/Sparkline.mjs` if it maintains its own map) to clean up the internal state and stop render loops.
34+
35+
## Timeline
36+
37+
- 2026-02-03T13:01:22Z @tobiu added the `bug` label
38+
- 2026-02-03T13:01:22Z @tobiu added the `ai` label
39+
- 2026-02-03T13:01:53Z @tobiu added parent issue #8930
40+
- 2026-02-03T13:10:42Z @tobiu referenced in commit `05a855c` - "Fix: Memory Leak in Neo.component.Canvas due to missing destroy() (#8961)"
41+
### @tobiu - 2026-02-03T13:10:55Z
42+
43+
**Input from Gemini 3 Pro:**
44+
45+
> ✦ I have fixed the memory leak in `Neo.component.Canvas` and `Neo.component.Sparkline`.
46+
>
47+
> **Changes:**
48+
> 1. **`src/worker/Canvas.mjs`**: Added `unregisterCanvas` to remove canvas references from the worker's internal map.
49+
> 2. **`src/component/Canvas.mjs`**:
50+
> - Implemented `destroy()` to call `unregisterCanvas` if the canvas was registered.
51+
> - Updated `afterSetMounted()` to call `unregisterCanvas` when the component is unmounted (value === false).
52+
> 3. **`src/canvas/Sparkline.mjs`**:
53+
> - Added `unregister` remote method to cleanup the renderer's specific state (active render loops, item maps).
54+
> 4. **`src/component/Sparkline.mjs`**:
55+
> - Implemented `destroy()` to call `renderer.unregister`.
56+
> - Updated `afterSetMounted()` to call `renderer.unregister` on unmount.
57+
>
58+
> **Outcome:**
59+
> `OffscreenCanvas` contexts and worker-side render loops are now properly cleaned up when a component is destroyed or unmounted (e.g., via `removeDom: true`). This prevents memory leaks and ensures long-term stability.
60+
61+
- 2026-02-03T13:11:47Z @tobiu assigned to @tobiu
62+
- 2026-02-03T13:12:00Z @tobiu closed this issue
63+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
id: 8962
3+
title: 'Perf: Apply CSS containment to Grid Rows'
4+
state: CLOSED
5+
labels:
6+
- enhancement
7+
- ai
8+
- performance
9+
assignees:
10+
- tobiu
11+
createdAt: '2026-02-03T13:01:26Z'
12+
updatedAt: '2026-02-03T13:18:18Z'
13+
githubUrl: 'https://github.com/neomjs/neo/issues/8962'
14+
author: tobiu
15+
commentsCount: 1
16+
parentIssue: 8930
17+
subIssues: []
18+
subIssuesCompleted: 0
19+
subIssuesTotal: 0
20+
blockedBy: []
21+
blocking: []
22+
closedAt: '2026-02-03T13:18:18Z'
23+
---
24+
# Perf: Apply CSS containment to Grid Rows
25+
26+
Since `Neo.grid.Body` manages row positions via absolute positioning (`transform`), the rows are independent of each other for layout purposes.
27+
28+
**Task:**
29+
Add `contain: strict` (or `contain: layout paint`) to the `.neo-grid-row` CSS class.
30+
31+
**Benefits:**
32+
This informs the browser that DOM updates within a row (e.g., text changes, canvas updates) do not affect the layout of the parent or siblings. This significantly reduces the cost of layout recalculations during heavy scrolling or real-time data updates.
33+
34+
**Files:**
35+
- `resources/scss/src/grid/Body.scss`
36+
37+
## Timeline
38+
39+
- 2026-02-03T13:01:27Z @tobiu added the `enhancement` label
40+
- 2026-02-03T13:01:27Z @tobiu added the `ai` label
41+
- 2026-02-03T13:01:28Z @tobiu added the `performance` label
42+
- 2026-02-03T13:01:55Z @tobiu added parent issue #8930
43+
- 2026-02-03T13:16:35Z @tobiu referenced in commit `4968f49` - "Perf: Apply CSS containment to Grid Rows (#8962)"
44+
### @tobiu - 2026-02-03T13:17:33Z
45+
46+
**Input from Gemini 3 Pro:**
47+
48+
> ✦ I have applied CSS containment optimizations to `Neo.grid.Body`.
49+
>
50+
> **Changes:**
51+
> 1. **`.neo-grid-row`**: Applied `contain: strict`. Since grid rows have explicit dimensions managed by the VDOM engine, we can safely use strict containment (layout + paint + size + style) to isolate them completely from the parent layout.
52+
> 2. **`.neo-grid-cell`**: Applied `contain: layout paint style`. Since cells may use `width: fit-content`, we cannot use `contain: size`, but we can still isolate their layout and painting from the rest of the row.
53+
> 3. **`.neo-grid-row`**: Added `will-change: transform` to hint the browser about the heavy usage of transforms for row positioning.
54+
>
55+
> **Outcome:**
56+
> Significant reduction in layout recalculation costs during scrolling and cell updates, as changes are now scoped to the individual row or cell boundary.
57+
58+
- 2026-02-03T13:18:04Z @tobiu assigned to @tobiu
59+
- 2026-02-03T13:18:19Z @tobiu closed this issue
60+

0 commit comments

Comments
 (0)