Skip to content

Latest commit

 

History

History
85 lines (81 loc) · 14.9 KB

File metadata and controls

85 lines (81 loc) · 14.9 KB

AGENTS.md

Learned User Preferences

  • Always respond in the language the user is writing in
  • Always update this AGENTS.md with new insights after code changes (workspace facts, patterns, preferences)
  • Always write AGENTS.md content in English

Learned Workspace Facts

  • Monorepo using yarn as package manager
  • Testing framework: vitest with coverage via yarn coverage
  • Global Vitest setup mocks were removed; tests now mock dependencies locally per file
  • Shared test mock modules like src/test/mocks/three.ts and src/test/mocks/three-spritetext.ts were removed; tests should inline only the mocks they actually need
  • Old global-mock cleanup can leave behind no-op local shims like vi.mock('three', () => importActual('three')); remove them when a test does not override Three behavior
  • After the WebGPU import migration, tests must mock three/webgpu when the production module imports from three/webgpu; mocking three does not affect those modules
  • TransformTool must add and traverse TransformControls.getHelper(), not the TransformControls instance itself, because current Three typings and runtime treat the control as non-Object3D
  • Local mocks for three/examples/jsm/* should use the exact runtime specifier including the .js suffix when the source import does
  • ARQuickLook tests must mock @shopware-ag/dive/assetloader and @shopware-ag/dive/assetexporter in addition to AssetConverter, because new AssetLoader() and new AssetExporter() are evaluated before the mocked AssetConverter constructor runs
  • DIVEGizmo tests should mock child gizmo classes as real Object3D instances with spied methods to avoid THREE.Object3D.add warnings from plain-object stand-ins
  • DIVEPrimitive tests are more stable with real Box3 plus per-test spies on Box3.prototype/Raycaster, instead of mocking the full three module surface
  • OrientationDisplayAxes tests should locally stub three-spritetext because jsdom does not implement the canvas text context that the real package needs
  • DIVERoot should detach both legacy scene-level TransformControls objects and modern TransformControlsRoot.controls helper roots when cleaning up transform controls
  • DIVERoot POV update/delete coverage requires manually seeding a matching Object3D in tests because addSceneObject intentionally skips creating POV scene nodes
  • Plugins live in src/plugins/<name>/ and are auto-discovered by looking for index.ts in subdirectories
  • Plugins are exported as subpath exports: @shopware-ag/dive/<plugin-name> (e.g. @shopware-ag/dive/shader, @shopware-ag/dive/state)
  • The shader plugin (src/plugins/shader/) now exposes node-based building blocks like GridNode and GridNodeUniforms; legacy DIVEShaderLib/DIVEShaderMaterial shader-lib wrappers are being removed
  • DIVEGrid custom shader code must use TSL/node materials for WebGPU; plain ShaderMaterial triggers THREE.NodeMaterial: Material "ShaderMaterial" is not compatible
  • DIVEGrid owns its MeshBasicNodeMaterial setup and creates its grid uniform nodes locally; GridNode only provides the TSL output-node implementation
  • DIVEGrid component uses the shader plugin; it is imported transitively via SceneGrid@shopware-ag/dive/shader
  • Shader plugin public docs must describe the new node-based API: GridNode plus GridNodeUniforms; legacy DIVEShaderLib/DIVEShaderMaterial docs are outdated
  • Tests that mock @shopware-ag/dive/shader must provide a GridNode constructor stub after the shader plugin migration; legacy DIVEShaderLib-only mocks break transitive imports
  • Most tests do not need to mock @shopware-ag/dive/shader at all; after the WebGPU migration the only current direct need is src/components/grid/__test__/Grid.test.ts, which asserts DIVEGrid constructs GridNode
  • When partially mocking three/webgpu, base the mock on importOriginal<typeof import('three/webgpu')>(); using vi.importActual('three') drops WebGPU-only exports like Node and breaks transitive shader imports
  • DIVEGrid tests or other MeshBasicNodeMaterial mocks must preserve the constructor outputNode param because production code passes new GridNode(uniforms) directly into material creation
  • GridNode unit tests are best written with local three/tsl and three/webgpu mocks plus vi.hoisted(...); plain top-level mock helpers break because vi.mock(...) factories are hoisted
  • GridNode returns the final vec4(...) TSL node from its constructor while still naming the underlying base Node instance GridNode
  • In GridNode tests, keep a raw mock-uniform object separate from the GridNodeUniforms cast; casting too early hides Vitest .mock metadata from TypeScript
  • DIVEEnvironment no longer applies HDR state from the constructor alone; tests should wait for the async HDR load and call env.init() before asserting environment/background updates
  • DIVEEnvironment concurrent-load cleanup is best covered by spying on the private loadHDRImage method and resolving overlapping promises out of order; stale textures should be disposed
  • DIVE tests must mock mainView.renderer.initialized and mainView.renderer.init() because DIVE.start() now guards rendering via renderer initialization
  • On the v3 branch, deprecated compatibility APIs should not be kept alive just to satisfy tests; remove the matching legacy test coverage instead of restoring DIVE.QuickView(), engine, createView(), disposeView(), AnimationSystem.animate(), Toolbox.useTool(), Toolbox.getActiveTool(), or old environment no-op methods
  • DIVERenderer tests must mock three/webgpu WebGPURenderer; old three WebGLRenderer expectations are outdated
  • DIVERenderer stale-init behavior after setCanvas() is best tested with a deferred first init() promise; the old renderer must not trigger a second environment init after the swap
  • MediaCreator fallback coverage is easiest by overriding test canvas width/height to undefined and writable: true, then letting drawCanvas() fall back through clientWidth to the renderer canvas dimensions
  • MediaCreator.drawCanvas() must restore the previous WebGPU render target and camera layer mask before awaiting readRenderTargetPixelsAsync(); otherwise the live View.tick() render can keep drawing into the offscreen target and trigger WebGPUTextureUtils: Texture already initialized.
  • Demo fixture /Users/f.frank/Public/Repos/dive-demo/public/model_reverse_animation_order_long_name_blank_name.glb is used for animation edge cases; it contains a blank clip name, an overlong clip name, and a Walk clip that now hard-fails loading via an invalid animation accessor reference
  • yarn build can still exit successfully while vite-plugin-dts reports TypeScript API migration errors, so WebGPU refactors need explicit grep/type-review and not just a green build exit code
  • DIVE.start() is now a fire-and-forget wrapper around startAsync(), so tests that need renderer readiness should await startAsync() or a microtask before asserting downstream effects
  • DIVE.dispose() must dispose the DIVEClock before tearing down views/renderers, and startAsync() must bail out after late renderer init if the instance was disposed; otherwise demo route switches can leave stale RAF ticks calling DIVEView.tick() on dead WebGPU renderers
  • Demo views in /Users/f.frank/Public/Repos/dive-demo/src/views/ that create QuickView instances must dispose them in onUnmounted; missing route-leave cleanup leaves old WebGPU render loops alive across example switches
  • Deprecated BaseTool coverage was removed entirely; if src/plugins/toolbox/src/BaseTool.ts is gone in a future major, delete the legacy suite instead of recreating the class for tests
  • MediaCreator screenshot generation is async under WebGPU and uses RenderTarget plus readRenderTargetPixelsAsync; it no longer swaps renderer.domElement
  • DIVEXRLightRoot currently guards XREstimatedLight off under WebGPU and falls back to the existing scene light until a dedicated WebGPU-compatible light-estimation path exists
  • Library builds must externalize three with a pattern that also matches subpaths like three/webgpu, three/tsl, and three/examples/jsm/*; externalizing only bare three bundles a second Three runtime into build/ and triggers THREE.WARNING: Multiple instances of Three.js being imported. in consumers
  • State action migrations must use AnimationSystem.fromTargets(...).play() and Toolbox.enableTool(); lingering animate() or useTool() calls can still let yarn build exit 0 while vite-plugin-dts reports TS2339 API drift
  • When swapping canvases under WebGPU, DIVEEnvironment.setRenderer() must run before disposing the previous WebGPURenderer; disposing the old renderer first can crash PMREMGenerator.dispose() inside Three's NodeManager.delete with usedTimes access errors
  • OrientationDisplay.tick() should size its overlay viewport from DIVERenderer.canvas.clientHeight and restore the prior webgpurenderer.autoClear value; unit tests can fall back to the saved viewport height when the mock omits canvas
  • Neighboring dive-demo local verification can use a node_modules/@shopware-ag/dive symlink to this repo when yalc is absent, as long as this repo's build/ artifacts are present
  • The dive-demo orientation display example now uses a single QuickView canvas with displayAxes: true; the previous side-by-side comparison against a manually wired OrientationDisplay plugin is no longer the expected snapshot shape
  • dive-demo views that gate UI interactivity on QuickView readiness, such as DiveSwitchCanvas and DiveTargetAnimation, need to wait for a non-zero canvas layout plus a small initial delay before constructing QuickView; on CI Linux + xvfb + llvmpipe, starting too early leaves control buttons permanently disabled
  • In dive-demo, replacing the fixed QuickView startup sleep with a shared layout-driven wait helper (ResizeObserver plus animation-frame verification) keeps the initial load stable, but canvas-switch flows still need to yield one DOM frame after committing the active-panel state before calling mainView.setCanvas(...)
  • DIVECanvasLifecycleManager in src/engine/canvas/ is again the single owner of canvas readiness state: it keeps the waiter promises, resolves waitForHealthyCanvas(), and advances readiness via its own tick()
  • DIVECanvasLifecycleManager.tick() must early-return while the current canvas remains valid; only invalid, detached, or freshly swapped canvases should re-enter the two-sample stabilization path
  • DIVECanvasLifecycleManager.tick() should stay as a shallow entrypoint that does the dispose guard and then delegates the actual lifecycle progression to the private _checkCanvasHealth() helper for readability
  • DIVEView.tick() should always call DIVECanvasLifecycleManager.tick() before honoring the paused/render path so canvas readiness can continue progressing even while rendering is paused
  • DIVECanvasLifecycleManager keeps its layout/readiness helpers as private member methods instead of top-level module helpers, so the canvas lifecycle logic stays co-located inside the class
  • DIVEView.init(), DIVERenderer.init(), and DIVEEnvironment.init() should stay async and explicitly await their cached _initPromise values; this repo prefers the consistent async method shape over collapsing those branches to direct promise returns
  • DIVECanvasLifecycleManager.waitForHealthyCanvas() can take an optional AbortSignal; aborting resolves only that individual waiter with null, while the CLM's shared readiness state keeps progressing through later tick() calls
  • DIVEView now uses an internal AbortController to invalidate pending init work on dispose() and setCanvas(); even with abort support, renderer !== this._renderer remains as the stale-renderer guard after awaited renderer initialization
  • DIVERenderer no longer owns DOM/canvas readiness logic; it only initializes WebGPU/environment state, swaps canvases, and handles render/resize calls
  • The old DIVEResizeManager compatibility layer has been removed entirely on v3; canvas ownership now lives directly between DIVEView and DIVECanvasLifecycleManager
  • DIVEView.setCanvas() must not force an immediate onResize() on the swapped canvas; the DIVECanvasLifecycleManager is the single source of truth for resize propagation
  • DIVECanvasLifecycleManager.setCanvas() must reset its cached width/height so an equally sized replacement canvas still emits the initial resize sync for the new renderer/camera pair
  • In DIVECanvasLifecycleManager, keep raw measurement in _getCanvasLayout() and the valid-layout fast path inside waitForHealthyCanvas()/tick(); there is no longer a separate public readiness accessor
  • DIVEView should pass a named _handleCanvasResize callback into DIVECanvasLifecycleManager instead of an inline lambda, so the renderer/camera resize orchestration stays explicit while the CLM remains decoupled
  • DIVEView invalidation branches after async init are best covered by disposing the view while renderer.init() is still pending and by invoking the DIVECanvasLifecycleManager resize callback directly to assert the onResize + immediate render path
  • DIVECanvasLifecycleManager keeps a single steady-state ResizeObserver on the canvas itself; parent changes are handled in tick() as validity invalidations rather than as observed resize events
  • DIVEView does not inject the clock into DIVECanvasLifecycleManager; only DIVEView itself is a DIVETicker, and DIVE.startAsync() must start the DIVEClock before awaiting mainView.init() so the CLM's internal tick() can progress inside the view loop
  • DIVECanvasLifecycleManager coverage is easiest to keep at 100% with explicit tick() advancement in tests, observer invalidation cases, and signal-based waiter success/stale-resolution assertions
  • In View.test.ts, the waitForHealthyCanvas mock should be explicitly typed as Promise<DIVECanvasLayout | null>; otherwise the stale null path triggers a TypeScript error on mockResolvedValue(null)
  • DIVECanvasLifecycleManager now keeps its shared waiter state under _healthyCanvasPromise and _resolveHealthyCanvas so the promise naming matches the canvas-health narrative
  • In Dive.test.ts, mainView.init is typed as a plain async method, so tests should narrow it with vi.mocked(...) before calling mock-only helpers like mockRejectedValueOnce or mockImplementationOnce
  • Full focused coverage for CanvasLifecycleManager.ts now needs explicit tests for parentless bootstrap polling, renderable-to-zero resets during stabilization, same-size canvas swaps, waiter-only aborts, and the private direct-layout fallback after bootstrap completion
  • Focused single-file coverage in this repo should use vitest --coverage.include=<path>; running one suite with the default global src/**/* coverage scope still enforces repo-wide thresholds and will fail even when the targeted file itself is at 100%