Skip to content

Conversation

@mateuszJS
Copy link
Owner

@mateuszJS mateuszJS commented Jun 14, 2025

Summary by CodeRabbit

  • New Features
    • Improved asset management by automatically assigning asset IDs and removing the need for manual ID handling when adding images.
    • Enhanced cleanup process with a new destroy function for proper resource deallocation.
  • Bug Fixes
    • Prevented unhandled errors during GPU buffer mapping, improving stability during device or buffer destruction.
  • Refactor
    • Simplified and unified asset and callback naming conventions for consistency.
    • Updated ESLint configuration for improved code style and TypeScript support.
  • Chores
    • Enabled source map generation for production builds to aid in debugging.
    • Updated type definitions and interfaces to reflect changes in asset handling and function signatures.

@coderabbitai
Copy link

coderabbitai bot commented Jun 14, 2025

Walkthrough

This change removes explicit asset ID management from the TypeScript and Zig interfaces, shifting asset identification to internal logic. It updates asset-related type definitions, callbacks, and function signatures accordingly. The ESLint configuration is refactored and simplified. Additional improvements include enhanced cleanup routines, error handling in GPU picking, and always enabling production source maps.

Changes

File(s) Change Summary
eslint.config.mjs, eslint.config copy.mjs Deleted legacy ESLint configuration files.
eslint.config.js Added new ESLint config combining recommended, TypeScript, and stylistic rules.
jestWgslTransformer.js Refactored from CommonJS to ES module export style.
package.json Updated types field path for TypeScript declarations.
webpack.config.js Production builds now always generate source maps.
integration-tests/index.ts Removed explicit asset ID logic from image addition and asset handling.
src/index.ts Updated asset types, removed asset ID from API, improved cleanup on destroy.
src/logic/index.d.ts Renamed types/functions, removed asset ID, added destroy_state.
src/logic/index.zig Asset IDs now managed internally; updated callback, cleanup, and function signatures.
src/logic/texture.zig Added AssetZig struct and serialization method.
src/run.ts Renamed import, made animation cleanup explicit, updated return type.
src/WebGPU/pick.ts Improved formatting, error handling, and minor signature changes.

Sequence Diagram(s)

sequenceDiagram
    participant TS as TypeScript (src/index.ts)
    participant Zig as Zig Logic (index.zig)
    participant GPU as WebGPU
    participant UI as User Interface

    UI->>TS: User adds image
    TS->>Zig: addImage(img, points?)
    Zig->>Zig: Assigns internal asset ID, stores asset
    Zig->>TS: on_asset_update_callback([AssetZig])
    TS->>UI: Updates asset list (no explicit IDs)
    UI->>TS: Calls destroy()
    TS->>Zig: destroy_state()
    TS->>GPU: Unconfigure context, destroy device
Loading

Possibly related PRs

  • mateuszJS/magic-render#11: Related rewrite of asset management and rendering logic from Rust to Zig, overlapping with asset ID and callback changes.
  • mateuszJS/magic-render#9: Related asset ID and picking logic changes, but with opposite approaches to asset ID usage.
  • mateuszJS/magic-render#13: Introduces asset state saving/loading and asset update callback with explicit asset IDs; this PR is directly related as the current changes remove explicit asset ID handling and update callback signatures, affecting shared logic.

Suggested labels

released on @next

Poem

A hop, a skip, IDs away,
Now assets find their path today.
ESLint shines with rules anew,
Source maps guide us straight and true.
With cleanup neat and picking strong,
The code hops forward, right along!
🐇✨

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

npm error Exit handler never called!
npm error This is an error with npm itself. Please report this error at:
npm error https://github.com/npm/cli/issues
npm error A complete log of this run can be found in: /.npm/_logs/2025-06-14T14_08_34_667Z-debug-0.log


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc0dfe1 and 4550463.

📒 Files selected for processing (4)
  • src/index.ts (3 hunks)
  • src/logic/index.d.ts (1 hunks)
  • src/logic/index.zig (8 hunks)
  • src/logic/texture.zig (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/logic/index.d.ts
  • src/index.ts
  • src/logic/texture.zig
  • src/logic/index.zig
✨ Finishing Touches
  • 📝 Generate Docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🔭 Outside diff range comments (1)
src/logic/texture.zig (1)

28-33: 🛠️ Refactor suggestion

Allocated vertex buffers are never freed

Both get_vertex_data and get_vertex_pick_data use std.heap.page_allocator.alloc without a corresponding free.
This is inside a hot path and can quickly exhaust memory when assets are updated frequently.

Add a de-allocator or switch to a caller-supplied buffer.

🧹 Nitpick comments (10)
jestWgslTransformer.js (1)

1-5: Unused options parameter – please remove or prefix with _

options is accepted but never referenced. This will trigger the no-unused-vars rule once ESLint is wired up.
Two quick fixes:

-export const process = (sourceText, sourcePath, options) => {
+export const process = (sourceText, sourcePath /* options _unused_ */) => {

or

-export const process = (sourceText, sourcePath, options) => {
+export const process = (sourceText, sourcePath) => {
eslint.config.js (1)

9-14: Stylistic plugin is declared but not enabled

@stylistic/js is added to plugins, but none of its rules are turned on.
If you intended to leverage the preset, add something like:

rules: {
  ...stylisticJs.configs.custom, // or whichever stylistic preset you need
}

Otherwise the plugin inclusion is dead weight.

src/logic/texture.zig (1)

10-13: Struct name leaks implementation detail

AssetZig bakes the language name into the public API. If the struct is meant to cross the JS boundary, consider a neutral name (Asset or SerializedAsset) to avoid later churn if the backend changes again.

integration-tests/index.ts (1)

55-55: creator.addImage is async-unsafe

URL.createObjectURL is revoked only when the page unloads.
Consider revoking after the texture is uploaded to free memory:

img.onload = () => {
  creator.addImage(img).finally(() => URL.revokeObjectURL(img.src))
}
src/run.ts (3)

34-35: Avoid sentinel 0 → initialise rafId lazily

requestAnimationFrame never returns 0, but passing 0 to cancelAnimationFrame is still a (no-op) call.
Initialising to undefined (or null) removes the redundant cancel and makes types clearer.

-  let rafId = 0
+  let rafId: number | undefined

55-58: Duplicate frame-scheduling can be trimmed

requestAnimationFrame(draw) is invoked both at the end of draw (line 55) and once more immediately after the function definition (line 58).
That’s correct for an rAF loop, but the latter call can race with a very fast first loop and momentarily schedule two frames.
Instead, start the loop once and have draw reschedule itself:

-    rafId = requestAnimationFrame(draw)
+    rafId = requestAnimationFrame(draw)
...
-  rafId = requestAnimationFrame(draw)
+  draw(performance.now() as DOMHighResTimeStamp) // kickoff without double-queueing

60-62: Expose a no-op guard in the cleanup closure

If stopCreator is called twice we’ll attempt to cancel the same frame ID twice.
A quick guard prevents accidental double invocations:

-  return () => {
-    cancelAnimationFrame(rafId)
-  }
+  return () => {
+    if (rafId !== undefined) {
+      cancelAnimationFrame(rafId)
+      rafId = undefined
+    }
+  }
src/logic/index.d.ts (1)

34-34: Typo: destory_statedestroy_state

The misspelling propagates to every TS consumer and the Zig export.
Aligning the name now avoids permanent API baggage.

-  export const destory_state: VoidFunction
+  export const destroy_state: VoidFunction
src/WebGPU/pick.ts (1)

108-116: Minor catch-block polish & typo

Spelling and spacing nit; also console.debug(err) could aid sporadic GPU-device loss debugging without polluting normal logs.

-      /* ignorign errors when map fails because device was destroyed(so buffer too and was unmapped before mapAsync completed)*/
+      // ignoring errors when map fails because the device/buffer was destroyed
+      // console.debug('pick mapAsync failed', err)
src/index.ts (1)

58-69: Callback array copy allocates every frame

connect_on_asset_update_callback may fire every pick frame; recreating and spreading arrays/object literals inside the callback can become GC-heavy.

Consider re-using typed arrays / objects or passing references and letting the consumer copy only when necessary.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f358103 and bc0dfe1.

📒 Files selected for processing (13)
  • eslint.config copy.mjs (0 hunks)
  • eslint.config.js (1 hunks)
  • eslint.config.mjs (0 hunks)
  • integration-tests/index.ts (1 hunks)
  • jestWgslTransformer.js (1 hunks)
  • package.json (1 hunks)
  • src/WebGPU/pick.ts (4 hunks)
  • src/index.ts (3 hunks)
  • src/logic/index.d.ts (1 hunks)
  • src/logic/index.zig (6 hunks)
  • src/logic/texture.zig (2 hunks)
  • src/run.ts (4 hunks)
  • webpack.config.js (1 hunks)
💤 Files with no reviewable changes (2)
  • eslint.config copy.mjs
  • eslint.config.mjs
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/run.ts (2)
src/WebGPU/pick.ts (1)
  • PickManager (7-119)
src/logic/index.d.ts (1)
  • connect_web_gpu_programs (24-29)
src/WebGPU/pick.ts (1)
src/logic/index.d.ts (1)
  • on_update_pick (19-19)
src/index.ts (4)
src/logic/index.d.ts (3)
  • connect_on_asset_update_callback (30-30)
  • add_texture (16-16)
  • destory_state (34-34)
src/WebGPU/getTexture/index.ts (1)
  • createTextureFromSource (106-117)
src/utils/getDefaultPoints.ts (1)
  • getDefaultPoints (3-19)
src/run.ts (1)
  • runCreator (11-63)
🔇 Additional comments (7)
eslint.config.js (1)

7-15: ⚠️ Potential issue

Second argument should be TypeScript-ESLint’s recommended preset, not ESLint’s

The call currently passes eslint.configs.recommended twice, so TypeScript-specific rules never get applied.

-export default tseslint.config(eslint.configs.recommended, eslint.configs.recommended, {
+export default tseslint.config(
+  eslint.configs.recommended,          // base JS rules
+  tseslint.configs.recommended,        // TypeScript rules
+  {
+    rules: {
+      'no-multiple-empty-lines': 'error',
+      semi: ['error', 'never'],
+    },
+    plugins: { '@stylistic/js': stylisticJs },
+  },
+)

Likely an incorrect or invalid review comment.

src/logic/texture.zig (1)

72-78: Missing id field can break picking / hit-testing

serialize() omits .id, yet get_vertex_pick_data still writes self.id into the vertex buffer (line 60).
If the JS side relies on the serialized payload for hit-testing, dropping the id will desynchronise the two paths.

 pub fn serialize(self: Texture) AssetZig {
     return AssetZig{
         .points = self.points,
         .texture_id = self.texture_id,
+        // .id = self.id, // ← consider including or document why it's safe to drop
     };
 }
webpack.config.js (1)

18-18: Consider hidden-source-map instead of source-map for production

'source-map' exposes the full original source in the published bundle.
If you only need stack-traces without revealing code, switch to 'hidden-source-map' (or 'nosources-source-map') to avoid leaking IP while keeping debuggability.

-  devtool: isProd ? 'source-map' : 'eval-source-map',
+  devtool: isProd ? 'hidden-source-map' : 'eval-source-map',
src/logic/index.d.ts (1)

16-18: update_points still exposes id although add_texture no longer does

With asset IDs now generated internally, external code has no stable handle to pass to update_points(id, …).
Either:

  1. Expose the generated ID from add_texture, or
  2. Change update_points to identify assets differently (e.g., by pointer or implicit “last”).

Please verify the new contract; otherwise updates will be impossible.

src/index.ts (1)

71-79: newTextureId race on slow image loads

newTextureId is derived from textures.length at img.onload time.
If multiple large images load out-of-order, IDs will mismatch the order of original assets[], potentially breaking deterministic re-loads.

Capture the index synchronously:

-    const newTextureId = textures.length
+    const newTextureId = textures.push({
+      url: img.src,
+      texture: createTextureFromSource(device, img, { flipY: true }),
+    }) - 1
-    textures.push({ … })

Push first, then use the returned index.

src/logic/index.zig (2)

18-22: Renaming follows Zig conventions – looks good.

Switching to snake_case for exported functions is consistent with idiomatic Zig style and avoids mixed-case exports on the TS side.


24-27: Callback signature update acknowledged, but verify all call-sites.

The callback now transmits []AssetZig. Double-check that every caller in the TS layer (and tests) has been updated; mismatched function pointers will SIGILL at runtime.

Comment on lines +63 to 67
var next_asset_id: u32 = ASSET_ID_TRESHOLD;
pub fn add_texture(points: [4]Types.PointUV, texture_id: u32) void {
state.assets.put(next_asset_id, Texture.new(next_asset_id, points, texture_id)) catch unreachable;
next_asset_id +%= 1;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Un-checked wrapping add risks ID reuse after overflow.

next_asset_id +%= 1 silently wraps on u32 overflow, eventually colliding with existing assets after 4 294 967 295 inserts (or sooner if IDs are recycled).
Prefer checked addition and fail explicitly, or switch to u64.

-    next_asset_id +%= 1;
+    next_asset_id += 1;
+    if (next_asset_id == 0) { // overflowed back to 0
+        @panic("asset id overflow");
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var next_asset_id: u32 = ASSET_ID_TRESHOLD;
pub fn add_texture(points: [4]Types.PointUV, texture_id: u32) void {
state.assets.put(next_asset_id, Texture.new(next_asset_id, points, texture_id)) catch unreachable;
next_asset_id +%= 1;
}
var next_asset_id: u32 = ASSET_ID_TRESHOLD;
pub fn add_texture(points: [4]Types.PointUV, texture_id: u32) void {
state.assets.put(next_asset_id, Texture.new(next_asset_id, points, texture_id)) catch unreachable;
next_asset_id += 1;
if (next_asset_id == 0) { // overflowed back to 0
@panic("asset id overflow");
}
}
🤖 Prompt for AI Agents
In src/logic/index.zig around lines 63 to 67, the increment of next_asset_id
uses wrapping addition which can silently overflow and cause ID reuse. Replace
the wrapping addition with a checked addition that detects overflow and
explicitly fails or handles the error. Alternatively, change the type of
next_asset_id to u64 to reduce overflow risk. Ensure the code prevents silent ID
collisions by handling the increment safely.

Comment on lines 223 to 230
pub fn destory_state() void {
state.assets.deinit();
next_asset_id = ASSET_ID_TRESHOLD;
web_gpu_programs = undefined;
on_asset_update_cb = undefined;
// state itself is not destoyed as it will be reinitalized before usage
// and has no reference to memory to free
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Typo and incomplete cleanup in destory_state.

  1. Function name is misspelled – should be destroy_state.
  2. Only the hashmap is de-initialised; any heap allocations owned by Texture instances (vertex buffers, etc.) are not released.
  3. Outstanding result buffers from the previous comment remain leaked.
-pub fn destory_state() void {
-    state.assets.deinit();
+pub fn destroy_state() void {
+    var it = state.assets.valueIterator();
+    while (it.next()) |tex| {
+        tex.deinit(); // assuming Texture owns heap memory
+    }
+    state.assets.deinit();

Add corresponding rename on the TS side.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
pub fn destory_state() void {
state.assets.deinit();
next_asset_id = ASSET_ID_TRESHOLD;
web_gpu_programs = undefined;
on_asset_update_cb = undefined;
// state itself is not destoyed as it will be reinitalized before usage
// and has no reference to memory to free
}
pub fn destroy_state() void {
var it = state.assets.valueIterator();
while (it.next()) |tex| {
tex.deinit(); // assuming Texture owns heap memory
}
state.assets.deinit();
next_asset_id = ASSET_ID_TRESHOLD;
web_gpu_programs = undefined;
on_asset_update_cb = undefined;
// state itself is not destoyed as it will be reinitalized before usage
// and has no reference to memory to free
}
🤖 Prompt for AI Agents
In src/logic/index.zig around lines 223 to 230, rename the function from
destory_state to destroy_state to fix the typo. Extend the cleanup logic to
properly release all heap allocations owned by Texture instances, such as vertex
buffers, to prevent memory leaks. Also, ensure any outstanding result buffers
are freed as indicated by previous comments. Finally, update the corresponding
function name on the TypeScript side to maintain consistency.

Comment on lines +99 to 105
var result = std.heap.page_allocator.alloc(AssetZig, state.assets.count()) catch unreachable;
var iterator = state.assets.iterator();
var i: usize = 0;
while (iterator.next()) |entry| {
result[i] = entry.value_ptr.*;
result[i] = entry.value_ptr.serialize();
i += 1;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Leaking allocator memory every pointer-up.

std.heap.page_allocator.alloc is never freed; result lives past the function scope with no matching dealloc, causing an unbounded leak during interactive sessions.

If the JS side copies the slice immediately, free it right after the callback:

     on_asset_update_cb(result);
-}
+    std.heap.page_allocator.free(result);
+}

Otherwise, expose a free_asset_update_buffer callback for the consumer to invoke.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var result = std.heap.page_allocator.alloc(AssetZig, state.assets.count()) catch unreachable;
var iterator = state.assets.iterator();
var i: usize = 0;
while (iterator.next()) |entry| {
result[i] = entry.value_ptr.*;
result[i] = entry.value_ptr.serialize();
i += 1;
}
on_asset_update_cb(result);
std.heap.page_allocator.free(result);
}
🤖 Prompt for AI Agents
In src/logic/index.zig around lines 99 to 105, the memory allocated with
std.heap.page_allocator.alloc for 'result' is never freed, causing a memory
leak. To fix this, either free the allocated memory immediately after the JS
side copies the slice if it does so synchronously, or provide a dedicated
'free_asset_update_buffer' function that the consumer can call to deallocate the
memory when it is no longer needed.

Repository owner deleted a comment from coderabbitai bot Jun 14, 2025
@mateuszJS mateuszJS merged commit 57d5bfc into next Jun 14, 2025
4 checks passed
@github-actions
Copy link

🎉 This PR is included in version 0.1.0-next.3 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants