diff --git a/app/src/views/panes/commitFetch.ts b/app/src/api/commit.ts similarity index 90% rename from app/src/views/panes/commitFetch.ts rename to app/src/api/commit.ts index 39c75ee1..f8d6237e 100644 --- a/app/src/views/panes/commitFetch.ts +++ b/app/src/api/commit.ts @@ -1,4 +1,4 @@ -// views/panes/commitFetch.ts — lazy fetcher for the full commit +// api/commit.ts — lazy fetcher for the full commit // message body. Called when the user clicks "Show full message" in // the commit pane. Author + subject are already in the manifest; // body comes from /api/commit on demand to keep the manifest small. diff --git a/app/src/utils/serverConfig.ts b/app/src/api/config.ts similarity index 95% rename from app/src/utils/serverConfig.ts rename to app/src/api/config.ts index 251a2c14..9e808e5d 100644 --- a/app/src/utils/serverConfig.ts +++ b/app/src/api/config.ts @@ -1,4 +1,4 @@ -// utils/serverConfig.ts — One-shot fetch of /api/config, memoized. +// api/config.ts — One-shot fetch of /api/config, memoized. // // Read once at boot in main.ts and passed into UI components that need // to know server-side feature flags (currently: whether local-repo diff --git a/app/src/utils/url.ts b/app/src/api/index.ts similarity index 52% rename from app/src/utils/url.ts rename to app/src/api/index.ts index d578f802..a9209055 100644 --- a/app/src/utils/url.ts +++ b/app/src/api/index.ts @@ -1,6 +1,6 @@ -// url.ts — Build API URLs from the page's query params. Pure function so -// it's directly unit-testable; main.ts wraps it with the live -// `window.location.*` values for runtime callers. +// api/index.ts — Shared API helpers. Endpoint-specific code (URL builders, +// fetchers, streaming) lives in sibling files (api/manifest.ts, +// api/commit.ts, api/config.ts). export interface BuildApiUrlOpts { noCache?: boolean; @@ -12,6 +12,10 @@ export interface BuildApiUrlOpts { * appends `no_cache=true` to force a fresh scan on this request. * When no `src` is present, returns the endpoint URL without any * source params — boot uses this to detect "no source picked yet". + * + * Pure function (no `window` access) so endpoint-specific wrappers + * in sibling modules can bind it to live `window.location.*` values + * while this helper stays directly unit-testable. */ export function buildApiUrl( endpoint: string, @@ -30,16 +34,3 @@ export function buildApiUrl( } return u.toString(); } - -// Runtime wrappers that bind `buildApiUrl` to the live `window.location.*` -// values. Kept here (rather than at callsites) so the boot orchestrator and -// the live-update poll loop share the same construction path. Pure helper -// stays separately testable via buildApiUrl. - -export function manifestUrl(opts: BuildApiUrlOpts = {}): string { - return buildApiUrl('/api/manifest', window.location.search, window.location.origin, opts); -} - -export function signatureUrl(): string { - return buildApiUrl('/api/manifest/signature', window.location.search, window.location.origin); -} diff --git a/app/src/utils/manifestStream.ts b/app/src/api/manifest.ts similarity index 73% rename from app/src/utils/manifestStream.ts rename to app/src/api/manifest.ts index a260da9e..3299442e 100644 --- a/app/src/utils/manifestStream.ts +++ b/app/src/api/manifest.ts @@ -1,4 +1,4 @@ -// NDJSON streaming reader for /api/manifest responses. Each line is a +// api/manifest.ts — NDJSON streaming reader for /api/manifest responses. Each line is a // single JSON event. The browser handles Content-Encoding: gzip // transparently, so we read decoded UTF-8 text directly. // @@ -17,6 +17,22 @@ // error — fatal mid-stream failure; client should surface and stop. import type { Manifest } from '@/types/manifest'; +import { buildApiUrl, type BuildApiUrlOpts } from './'; + +// ── Endpoint URL builders ──────────────────────────────────────────────── + +/** URL for the manifest stream endpoint, bound to the current page's + * `?src` (and optional `?branch`) query params. */ +export function manifestUrl(opts: BuildApiUrlOpts = {}): string { + return buildApiUrl('/api/manifest', window.location.search, window.location.origin, opts); +} + +/** URL for the lightweight manifest-signature poll endpoint. */ +export function signatureUrl(): string { + return buildApiUrl('/api/manifest/signature', window.location.search, window.location.origin); +} + +// ── NDJSON streaming reader ────────────────────────────────────────────── // One variant per discriminant value so TS narrows cleanly through // `if (event.phase === 'cloning' || event.phase === 'scanning')` etc. diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index 152d9a89..4898891a 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -16,22 +16,22 @@ // }); // coord.dispose(); -import { initAppHeader } from './views/shell/appHeader.js'; -import { initAppFooter } from './views/shell/appFooter.js'; -import { showLeftSidebar } from './views/shell/leftSidebar.js'; -import { showRightSidebar, hideRightSidebar } from './views/shell/rightSidebar.js'; -import { buildFilePreviewPane, humanLanguageFor } from './views/panes/filePreviewPane.js'; -import { buildCommitPane } from './views/panes/commitPane.js'; -import { buildStreetPane } from './views/panes/streetPane.js'; -import { sameDayCommitCount } from './views/widgets/commitMetrics.js'; -import { labelFromManifest } from './views/widgets/displayLabel.js'; -import { LIVE_UPDATES } from './config/index.js'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './store/liveStatus.js'; +import { initAppHeader } from './views/shell/appHeader'; +import { initAppFooter } from './views/shell/appFooter'; +import { showLeftSidebar } from './views/shell/leftSidebar'; +import { showRightSidebar, hideRightSidebar } from './views/shell/rightSidebar'; +import { buildFilePreviewPane, humanLanguageFor } from './views/panes/filePreviewPane'; +import { buildCommitPane } from './views/panes/commitPane'; +import { buildStreetPane } from './views/panes/streetPane'; +import { sameDayCommitCount } from './utils/commit'; +import { labelFromManifest } from './utils/sources'; +import { LIVE_UPDATES } from './state/settings/index'; +import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './state/runtime/liveStatus'; import { DateSource, NodeKind } from './types'; import type { DirNode, FileNode, PickTarget, TreeNode } from './types'; -import type { createWorld } from './scene/world.js'; -import type { createPicker } from './scene/system/picker.js'; -import type { createCameraRig } from './scene/system/cameraRig.js'; +import type { createWorld } from './scene/world'; +import type { createPicker } from './scene/system/picker'; +import type { createCameraRig } from './scene/system/cameraRig'; interface CoordinatorOpts { world: ReturnType; diff --git a/app/src/main.ts b/app/src/main.ts index 2326d756..38a872f2 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -4,31 +4,30 @@ import './styles.css'; -import * as Config from './config/index.js'; -import { REBUILD_STATUS } from './store/liveStatus.js'; -import { attachPersistence, persistAtomPerSource } from './store/persist.js'; -import { SYNTAX_THEME } from './config/prefs/syntaxTheme.js'; -import { sourceKey, CURRENT_SOURCE_KEY } from './store/sourceContext.js'; -import { attachCommitReactions } from './store/configCommitReactions.js'; -import { setupLiveUpdates } from './store/liveUpdates.js'; +import * as Config from './state/settings/index'; +import { REBUILD_STATUS } from './state/runtime/liveStatus'; +import { attachPersistence, persistAtomPerSource } from './state/persist'; +import { SYNTAX_THEME } from './state/settings/prefs/syntaxTheme'; +import { sourceKey, CURRENT_SOURCE_KEY } from './state/runtime/sourceContext'; +import { attachCommitReactions } from './state/reactions'; +import { setupLiveUpdates } from './state/runtime/liveUpdates'; import { DOM_IDS } from './constants'; import { NodeKind } from './types'; import type { Manifest } from './types'; -import { PICKER_SELECTION_KEY } from './scene/system/picker.js'; -import { manifestUrl } from './utils/url.js'; -import { _srcKind, _deriveLabel } from './utils/source.js'; -import { applyHljsTheme } from './utils/syntaxTheme.js'; -import { buildIconAtlas } from './scene/components/buildings/iconAtlas.js'; -import { setIconAtlas } from './scene/components/buildings/buildings.js'; -import { setCellIconAtlas } from './scene/components/buildings/buildingsCell.js'; -import { createSourcePicker, type SourcePayload } from './views/source/sourcePicker.js'; -import { createLoadingOverlay } from './views/source/loadingOverlay.js'; -import { streamManifest } from './utils/manifestStream.js'; -import { pushRecent } from './views/source/sourceRecents.js'; -import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js'; -import { labelFromUrl } from './views/widgets/displayLabel.js'; -import { getServerConfig } from './utils/serverConfig.js'; +import { PICKER_SELECTION_KEY } from './scene/system/picker'; +import { manifestUrl } from './api/manifest'; +import { srcKind, labelFromUrl } from './utils/sources'; +import { applyHljsTheme } from './utils/syntaxTheme'; +import { buildIconAtlas } from './scene/components/buildings/iconAtlas'; +import { setIconAtlas } from './scene/components/buildings/buildings'; +import { setCellIconAtlas } from './scene/components/buildings/buildingsCell'; +import { createSourcePicker, type SourcePayload } from './views/components/sourcePicker'; +import { createLoadingOverlay } from './views/components/loadingOverlay'; +import { streamManifest } from './api/manifest'; +import { pushRecent } from './state/runtime/sourceRecents'; +import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop'; +import { getServerConfig } from './api/config'; /** * Set document.title to "{label} (pending) — codecity" from a server-emitted @@ -128,8 +127,8 @@ if (_canvas) { const _bootSrc = qp.get('src')!; const _bootBranch = qp.get('branch') ?? undefined; loadingOverlay.show({ - kind: _srcKind(_bootSrc), - label: _deriveLabel(_bootSrc), + kind: srcKind(_bootSrc), + label: labelFromUrl(_bootSrc) ?? _bootSrc, branch: _bootBranch, }); try { @@ -260,8 +259,8 @@ if (_canvas) { handle.world.resetCache(); const dismissibleOnError = _lastDismissible; loadingOverlay.show({ - kind: _srcKind(payload.src), - label: _deriveLabel(payload.src), + kind: srcKind(payload.src), + label: labelFromUrl(payload.src) ?? payload.src, branch: payload.branch, }); try { @@ -327,7 +326,7 @@ if (_canvas) { // payload. handle.coordinator.setSourceInfo( payload.branch, - _srcKind(payload.src) === 'git' ? payload.src : undefined + srcKind(payload.src) === 'git' ? payload.src : undefined ); } manifest = event.manifest; @@ -383,7 +382,7 @@ if (_canvas) { // resolves to the just-applied manifest — otherwise the label is stale. handle.coordinator.setSourceInfo( resolvedBranch, - _srcKind(payload.src) === 'git' ? payload.src : undefined + srcKind(payload.src) === 'git' ? payload.src : undefined ); _liveUpdates?.setSignature(manifest.signature); @@ -391,7 +390,7 @@ if (_canvas) { src: payload.src, branch: resolvedBranch, branchIsDefault, - label: _deriveLabel(payload.src), + label: labelFromUrl(payload.src) ?? payload.src, }); if (!liveUpdatesStarted) { diff --git a/app/src/scene/components/adPanels/adPanelsInstanced.ts b/app/src/scene/components/adPanels/adPanelsInstanced.ts index c6508cbc..edb15ab9 100644 --- a/app/src/scene/components/adPanels/adPanelsInstanced.ts +++ b/app/src/scene/components/adPanels/adPanelsInstanced.ts @@ -11,12 +11,12 @@ // require WebGL2. import * as THREE from 'three'; -import { BuildingOrient } from '@/types/index.js'; -import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/config/index.js'; +import { BuildingOrient } from '@/types/index'; +import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/state/settings/index'; import { RENDER_ORDERS } from '@/constants'; -import { mediaKindOf, MediaKind } from './adPanels.js'; -import { AdPanelTextureArray, MAX_PAGES as AD_PANEL_MAX_PAGES } from './adPanelTextureArray.js'; -import type { Building } from '@/types/index.js'; +import { mediaKindOf, MediaKind } from './adPanels'; +import { AdPanelTextureArray, MAX_PAGES as AD_PANEL_MAX_PAGES } from './adPanelTextureArray'; +import type { Building } from '@/types/index'; import adPanelVertSrc from './adPanel.vert.glsl?raw'; import adPanelFragSrc from './adPanel.frag.glsl?raw'; diff --git a/app/src/scene/components/buildings/buildingColor.ts b/app/src/scene/components/buildings/buildingColor.ts index 6c7133de..e8958a68 100644 --- a/app/src/scene/components/buildings/buildingColor.ts +++ b/app/src/scene/components/buildings/buildingColor.ts @@ -13,7 +13,7 @@ // Tunables come from BUILDING_PALETTE in config/building.ts. Tests // mutate the store via .setKey() in setup + restore in teardown. -import { BUILDING_PALETTE } from '@/config/components/buildings.js'; +import { BUILDING_PALETTE } from '@/state/settings/components/buildings'; import { NodeKind } from '@/types'; // Structural shapes match what real Manifest tree / FileNode supply but diff --git a/app/src/scene/components/buildings/buildingIndex.ts b/app/src/scene/components/buildings/buildingIndex.ts index d1984b07..d7f5c833 100644 --- a/app/src/scene/components/buildings/buildingIndex.ts +++ b/app/src/scene/components/buildings/buildingIndex.ts @@ -9,8 +9,8 @@ // so reverse lookups are O(1). Inserts/removes update all three // maps atomically. -import type { Building } from '@/types/index.js'; -import type { DirNode } from '@/types/manifest.js'; +import type { Building } from '@/types/index'; +import type { DirNode } from '@/types/manifest'; export class BuildingIndex { readonly byPath = new Map(); diff --git a/app/src/scene/components/buildings/buildingTilt.ts b/app/src/scene/components/buildings/buildingTilt.ts index 5155ff8e..f7bd1d89 100644 --- a/app/src/scene/components/buildings/buildingTilt.ts +++ b/app/src/scene/components/buildings/buildingTilt.ts @@ -21,7 +21,7 @@ // or an inverse-shear ray transform (picker). import * as THREE from 'three'; -import { BUILDING_AGING } from '@/config/components/buildings.js'; +import { BUILDING_AGING } from '@/state/settings/components/buildings'; import type { Building } from '@/types'; /** diff --git a/app/src/scene/components/buildings/buildings.ts b/app/src/scene/components/buildings/buildings.ts index 45b6cf80..455e8c0f 100644 --- a/app/src/scene/components/buildings/buildings.ts +++ b/app/src/scene/components/buildings/buildings.ts @@ -17,9 +17,9 @@ import { LIGHTING, SCENE_COLORS, WINDOW_LIGHTING, -} from '@/config/index.js'; -import type { IconAtlas } from './iconAtlas.js'; -import { writeSunDir } from '@/scene/components/lighting/sunDir.js'; +} from '@/state/settings/index'; +import type { IconAtlas } from './iconAtlas'; +import { writeSunDir } from '@/scene/components/lighting/sunDir'; // --------------------------------------------------------------------------- // Per-instance facade attributes (window column count + door width) are diff --git a/app/src/scene/components/buildings/buildingsCell.ts b/app/src/scene/components/buildings/buildingsCell.ts index 2dde9c4d..c96dffce 100644 --- a/app/src/scene/components/buildings/buildingsCell.ts +++ b/app/src/scene/components/buildings/buildingsCell.ts @@ -10,15 +10,15 @@ // index/position buffers). import * as THREE from 'three'; -import { FACADE_GEOMETRY } from '@/config/index.js'; -import { BuildingOrient } from '@/types/index.js'; +import { FACADE_GEOMETRY } from '@/state/settings/index'; +import { BuildingOrient } from '@/types/index'; import buildingVertSrc from './building.vert.glsl?raw'; import buildingFragSrc from './building.frag.glsl?raw'; -import type { CellTile } from '../../layout/cellTile.js'; -import type { Building } from '@/types/index.js'; -import type { IconAtlas } from './iconAtlas.js'; -import { getFileIconName } from '@/views/widgets/fileIcon.js'; -import { seedFromPath, attachLeanAwareRaycast } from './buildingTilt.js'; +import type { CellTile } from '../../layout/cellTile'; +import type { Building } from '@/types/index'; +import type { IconAtlas } from './iconAtlas'; +import { getFileIconName } from '@/views/components/fileIcon'; +import { seedFromPath, attachLeanAwareRaycast } from './buildingTilt'; // --------------------------------------------------------------------------- // Shared geometry — unit box, constructed once at module load and diff --git a/app/src/scene/components/buildings/iconAtlas.ts b/app/src/scene/components/buildings/iconAtlas.ts index 783b8efe..9f5e4251 100644 --- a/app/src/scene/components/buildings/iconAtlas.ts +++ b/app/src/scene/components/buildings/iconAtlas.ts @@ -21,7 +21,7 @@ import * as THREE from 'three'; import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; -import { FILE_ICON_CDN_BASE, getFileIconName } from '@/views/widgets/fileIcon.js'; +import { FILE_ICON_CDN_BASE, getFileIconName } from '@/views/components/fileIcon'; // Atlas is 2048×2048 with 128-px slots → up to 16×16 = 256 unique // icons at 4× the per-icon resolution of the old 64-px slots. diff --git a/app/src/scene/components/fireflies/authorColor.ts b/app/src/scene/components/fireflies/authorColor.ts index ace15b47..0a9a73ef 100644 --- a/app/src/scene/components/fireflies/authorColor.ts +++ b/app/src/scene/components/fireflies/authorColor.ts @@ -8,7 +8,7 @@ // and the fireflies renderer (per-instance orb color). Same input = // same output across rebuilds and across consumers. -import { oklchToLinearRgb, linearRgbToHex } from '@/scene/utils/color/colors.js'; +import { oklchToLinearRgb, linearRgbToHex } from '@/scene/utils/color/colors'; // Base palette used for firefly orbs — saturated, mid-lightness. const ORB_L = 0.78; diff --git a/app/src/scene/components/fireflies/fireflies.ts b/app/src/scene/components/fireflies/fireflies.ts index 3323f42f..548933a5 100644 --- a/app/src/scene/components/fireflies/fireflies.ts +++ b/app/src/scene/components/fireflies/fireflies.ts @@ -3,12 +3,12 @@ // lifecycle handle. Mirrors trees/trees.ts. import * as THREE from 'three'; -import { placeFireflies, type FireflyPlacement } from './firefliesPlacement.js'; -import { createFireflyRenderer, type FireflyRenderer } from './firefliesRenderer.js'; -import { createOrbitRings } from './orbitRings.js'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; +import { placeFireflies, type FireflyPlacement } from './firefliesPlacement'; +import { createFireflyRenderer, type FireflyRenderer } from './firefliesRenderer'; +import { createOrbitRings } from './orbitRings'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; import type { CommitEntry } from '@/types'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; /** Public handle returned by createFireflies. Extends the renderer with * sha-based hover/select methods so callers don't need to manage the diff --git a/app/src/scene/components/fireflies/firefliesPlacement.ts b/app/src/scene/components/fireflies/firefliesPlacement.ts index 91bf414a..04cec28d 100644 --- a/app/src/scene/components/fireflies/firefliesPlacement.ts +++ b/app/src/scene/components/fireflies/firefliesPlacement.ts @@ -9,16 +9,16 @@ // encoding functions as treeRenderer, and read config defaults from TREES. import type { CommitEntry } from '@/types'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; -import { TREES } from '@/config/components/trees.js'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; +import { TREES } from '@/state/settings/components/trees'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; import { computeAgeRange, computeSizeRange, ageT, sizeT, -} from '@/scene/components/trees/treeEncoding.js'; -import { colorForAuthor, lightColorForAuthor } from './authorColor.js'; +} from '@/scene/components/trees/treeEncoding'; +import { colorForAuthor, lightColorForAuthor } from './authorColor'; export interface FireflyPlacement { /** Orbit center, world X. */ diff --git a/app/src/scene/components/fireflies/firefliesRenderer.ts b/app/src/scene/components/fireflies/firefliesRenderer.ts index 3c071f43..59c52375 100644 --- a/app/src/scene/components/fireflies/firefliesRenderer.ts +++ b/app/src/scene/components/fireflies/firefliesRenderer.ts @@ -9,8 +9,8 @@ import * as THREE from 'three'; import { RENDER_ORDERS } from '@/constants'; -import { FIREFLIES } from '@/config/components/fireflies.js'; -import type { FireflyPlacement } from './firefliesPlacement.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; +import type { FireflyPlacement } from './firefliesPlacement'; import vertexShader from './fireflies.vert.glsl?raw'; import fragmentShader from './fireflies.frag.glsl?raw'; diff --git a/app/src/scene/components/fireflies/orbitRings.ts b/app/src/scene/components/fireflies/orbitRings.ts index da8bdd5d..91ecf2a1 100644 --- a/app/src/scene/components/fireflies/orbitRings.ts +++ b/app/src/scene/components/fireflies/orbitRings.ts @@ -24,9 +24,9 @@ // dispose(): drop both slots' geometries + materials. import * as THREE from 'three'; -import { FIREFLIES } from '@/config/components/fireflies.js'; -import { RAINBOW } from '@/config/effects/effects.js'; -import type { FireflyPlacement } from './firefliesPlacement.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; +import { RAINBOW } from '@/state/settings/effects/effects'; +import type { FireflyPlacement } from './firefliesPlacement'; const TUBULAR_SEGMENTS = 96; // segments around the loop const RADIAL_SEGMENTS = 6; // segments around the tube's cross-section diff --git a/app/src/scene/components/footprint/footprint.ts b/app/src/scene/components/footprint/footprint.ts index 8b4fc7f5..479018e7 100644 --- a/app/src/scene/components/footprint/footprint.ts +++ b/app/src/scene/components/footprint/footprint.ts @@ -27,7 +27,7 @@ // refresh() handles COLOR, CORNER_RADIUS, and ENABLED only. import * as THREE from 'three'; -import { FOOTPRINT } from '@/config/components/footprint.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; import { RENDER_ORDERS } from '@/constants'; import { StreetAxis } from '@/types'; import type { Building, CityLayout, Street } from '@/types'; diff --git a/app/src/scene/components/gem/gem.ts b/app/src/scene/components/gem/gem.ts index d6733a26..954d4b3b 100644 --- a/app/src/scene/components/gem/gem.ts +++ b/app/src/scene/components/gem/gem.ts @@ -11,9 +11,14 @@ // just builds the static structure; mutation lives elsewhere. import * as THREE from 'three'; -import { GEM_SIZING, GEM_FACE_PALETTE, GEM_APPEARANCE, GEM_GLOW } from '@/config/components/gem.js'; +import { + GEM_SIZING, + GEM_FACE_PALETTE, + GEM_APPEARANCE, + GEM_GLOW, +} from '@/state/settings/components/gem'; import { NodeKind } from '@/types'; -import { gemAnchorXZ } from '../../utils/gemAnchor.js'; +import { gemAnchorXZ } from '../../utils/gemAnchor'; import type { Street } from '@/types'; // Procedural glow texture: a single-channel radial gradient drawn on a diff --git a/app/src/scene/components/island/islandMesh.ts b/app/src/scene/components/island/islandMesh.ts index 804cdfa6..260941b0 100644 --- a/app/src/scene/components/island/islandMesh.ts +++ b/app/src/scene/components/island/islandMesh.ts @@ -9,10 +9,10 @@ // island.dispose(); import * as THREE from 'three'; -import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/config/components/island.js'; -import { getWorldBounds, type WorldBounds } from '@/scene/layout/worldBounds.js'; -import { buildIslandGeometry, type IslandBuildParams } from './islandGeometry.js'; -import { createIslandMaterial } from './islandShader.js'; +import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island'; +import { getWorldBounds, type WorldBounds } from '@/scene/layout/worldBounds'; +import { buildIslandGeometry, type IslandBuildParams } from './islandGeometry'; +import { createIslandMaterial } from './islandShader'; import { RENDER_ORDERS } from '@/constants'; const ISLAND_TOP_Y = -2.0; // Increased from -0.5 for z-fighting prevention (4x separation from city y=0) diff --git a/app/src/scene/components/island/islandShader.ts b/app/src/scene/components/island/islandShader.ts index e17c46b6..61c06f40 100644 --- a/app/src/scene/components/island/islandShader.ts +++ b/app/src/scene/components/island/islandShader.ts @@ -6,7 +6,7 @@ // day/night cycle. import * as THREE from 'three'; -import { ISLAND_MATERIALS } from '@/config/components/island.js'; +import { ISLAND_MATERIALS } from '@/state/settings/components/island'; const vertSrc = /* glsl */ ` attribute vec3 color; diff --git a/app/src/scene/components/labels/labelAtlas.ts b/app/src/scene/components/labels/labelAtlas.ts index 4252364c..3e7ad57c 100644 --- a/app/src/scene/components/labels/labelAtlas.ts +++ b/app/src/scene/components/labels/labelAtlas.ts @@ -12,7 +12,7 @@ // from the atlas (writeLabelToSlot will treat missing rects as // zero-scale, i.e. invisible). The renderer warns but does not crash. -import type { LabelTypographyConfig } from '@/config/index.js'; +import type { LabelTypographyConfig } from '@/state/settings/index'; // --------------------------------------------------------------------------- // Public types diff --git a/app/src/scene/components/labels/labels.ts b/app/src/scene/components/labels/labels.ts index a820ef73..79a6a011 100644 --- a/app/src/scene/components/labels/labels.ts +++ b/app/src/scene/components/labels/labels.ts @@ -9,8 +9,8 @@ import * as THREE from 'three'; // Re-export shared atlas types + builder from the extracted module. -export type { LabelAtlasRect, LabelAtlasResult } from './labelAtlas.js'; -export { buildLabelAtlas } from './labelAtlas.js'; +export type { LabelAtlasRect, LabelAtlasResult } from './labelAtlas'; +export { buildLabelAtlas } from './labelAtlas'; // One ShaderMaterial per atlas texture (= per atlas page). world owns // the textures' lifetime; when it disposes them it calls diff --git a/app/src/scene/components/labels/labelsCell.ts b/app/src/scene/components/labels/labelsCell.ts index eaf0ee49..6a484502 100644 --- a/app/src/scene/components/labels/labelsCell.ts +++ b/app/src/scene/components/labels/labelsCell.ts @@ -15,11 +15,11 @@ // the shared atlas built by buildLabelAtlas() from labelAtlas.ts. import * as THREE from 'three'; -import { LABEL_TYPOGRAPHY } from '@/config/index.js'; +import { LABEL_TYPOGRAPHY } from '@/state/settings/index'; import { RENDER_ORDERS } from '@/constants'; -import type { Building } from '@/types/index.js'; -import type { CellTile } from '../../layout/cellTile.js'; -import type { LabelAtlasResult } from './labelAtlas.js'; +import type { Building } from '@/types/index'; +import type { CellTile } from '../../layout/cellTile'; +import type { LabelAtlasResult } from './labelAtlas'; import labelVertSrc from './label.vert.glsl?raw'; import labelFragSrc from './label.frag.glsl?raw'; diff --git a/app/src/scene/components/lighting/sunDir.ts b/app/src/scene/components/lighting/sunDir.ts index fb6d889c..f5aa8f23 100644 --- a/app/src/scene/components/lighting/sunDir.ts +++ b/app/src/scene/components/lighting/sunDir.ts @@ -16,7 +16,7 @@ // one-shot bakes like tree vertex shading). import * as THREE from 'three'; -import { LIGHTING } from '@/config/components/lighting.js'; +import { LIGHTING } from '@/state/settings/components/lighting'; export function writeSunDir(out: THREE.Vector3): void { const lighting = LIGHTING.get(); diff --git a/app/src/scene/components/repoLabel/repoLabel.ts b/app/src/scene/components/repoLabel/repoLabel.ts index 8ab3c639..f390df97 100644 --- a/app/src/scene/components/repoLabel/repoLabel.ts +++ b/app/src/scene/components/repoLabel/repoLabel.ts @@ -29,14 +29,14 @@ import * as THREE from 'three'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; -import { REPO_LABEL } from '@/config/components/repoLabel.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; import { RENDER_ORDERS } from '@/constants'; import vertSrc from './holoQuad.vert.glsl?raw'; import beamFragSrc from './holoBeam.frag.glsl?raw'; import textFragSrc from './holoText.frag.glsl?raw'; -import { createRepoNameTexture, redrawRepoName, type RepoNameTexture } from './textCanvas.js'; +import { createRepoNameTexture, redrawRepoName, type RepoNameTexture } from './textCanvas'; // Beam radius as a fraction of FONT_SIZE. Beam thickens automatically // when the user grows the label — keeps the beam reading as a real diff --git a/app/src/scene/components/sky/sky.ts b/app/src/scene/components/sky/sky.ts index 65708757..6ac630b5 100644 --- a/app/src/scene/components/sky/sky.ts +++ b/app/src/scene/components/sky/sky.ts @@ -22,8 +22,8 @@ // then composites everything on top. import * as THREE from 'three'; -import { SKY, SKY_STARS } from '@/config/components/sky.js'; -import { CAMERA_PERSPECTIVE } from '@/config/system/cameraRig.js'; +import { SKY, SKY_STARS } from '@/state/settings/components/sky'; +import { CAMERA_PERSPECTIVE } from '@/state/settings/system/cameraRig'; import { RENDER_ORDERS } from '@/constants'; import skyVertSrc from './sky.vert.glsl?raw'; diff --git a/app/src/scene/components/streets/streetLabels.ts b/app/src/scene/components/streets/streetLabels.ts index 038fb700..774201aa 100644 --- a/app/src/scene/components/streets/streetLabels.ts +++ b/app/src/scene/components/streets/streetLabels.ts @@ -16,7 +16,7 @@ // the camera orbits to the "upside-down" side. import * as THREE from 'three'; -import { ASPHALT, LABEL_TYPOGRAPHY } from '@/config/components/streets.js'; +import { ASPHALT, LABEL_TYPOGRAPHY } from '@/state/settings/components/streets'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind, StreetAxis } from '@/types'; import type { Street } from '@/types'; diff --git a/app/src/scene/components/streets/streetTile.ts b/app/src/scene/components/streets/streetTile.ts index 51845857..ecc05d70 100644 --- a/app/src/scene/components/streets/streetTile.ts +++ b/app/src/scene/components/streets/streetTile.ts @@ -5,7 +5,7 @@ // colors are baked in so no per-frame uniform updates are needed. import * as THREE from 'three'; -import type { SpatialGrid } from '@/scene/layout/spatialGrid.js'; +import type { SpatialGrid } from '@/scene/layout/spatialGrid'; export interface SidewalkRect { x: number; // centroid X diff --git a/app/src/scene/components/streets/streets.ts b/app/src/scene/components/streets/streets.ts index b96d9010..44929242 100644 --- a/app/src/scene/components/streets/streets.ts +++ b/app/src/scene/components/streets/streets.ts @@ -4,7 +4,7 @@ // the curved ends. import * as THREE from 'three'; -import { ASPHALT, SIDEWALK_COLORS } from '@/config/components/streets.js'; +import { ASPHALT, SIDEWALK_COLORS } from '@/state/settings/components/streets'; import { RENDER_ORDERS } from '@/constants'; import { CapStyle, JoinSide, NodeKind, StreetAxis } from '@/types'; import type { Street } from '@/types'; diff --git a/app/src/scene/components/trees/treePlacement.ts b/app/src/scene/components/trees/treePlacement.ts index 10f2cfca..41cce472 100644 --- a/app/src/scene/components/trees/treePlacement.ts +++ b/app/src/scene/components/trees/treePlacement.ts @@ -24,17 +24,17 @@ import RBush from 'rbush'; import * as THREE from 'three'; -import { TREES } from '@/config/components/trees.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; -import { ISLAND_GEOMETRY } from '@/config/components/island.js'; -import { getWorldBounds } from '../../layout/worldBounds.js'; -import { buildTopPolygon, pointInIslandPolygon } from '../island/islandGeometry.js'; -import { islandSeedFromBounds } from '../island/islandMesh.js'; +import { TREES } from '@/state/settings/components/trees'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; +import { ISLAND_GEOMETRY } from '@/state/settings/components/island'; +import { getWorldBounds } from '../../layout/worldBounds'; +import { buildTopPolygon, pointInIslandPolygon } from '../island/islandGeometry'; +import { islandSeedFromBounds } from '../island/islandMesh'; import { StreetAxis } from '@/types'; -import { gemAnchorXZ } from '../../utils/gemAnchor.js'; +import { gemAnchorXZ } from '../../utils/gemAnchor'; import type { Building, CityBbox, CityLayout, Street } from '@/types'; -import type { IslandGeometryConfig } from '@/config/components/island.js'; +import type { IslandGeometryConfig } from '@/state/settings/components/island'; interface Rect { minX: number; diff --git a/app/src/scene/components/trees/treePlacementClient.ts b/app/src/scene/components/trees/treePlacementClient.ts index b1c61af5..2d6856c5 100644 --- a/app/src/scene/components/trees/treePlacementClient.ts +++ b/app/src/scene/components/trees/treePlacementClient.ts @@ -6,13 +6,13 @@ // to a synchronous in-thread call when Worker is unavailable (older // browsers / SSR / test environments). -import { placeTrees, type TreePlacement } from './treePlacement.js'; -import { MSG } from './treePlacementProtocol.js'; -import { TREES } from '@/config/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; -import { ISLAND_GEOMETRY } from '@/config/components/island.js'; -import { WORLD } from '@/config/world/world.js'; +import { placeTrees, type TreePlacement } from './treePlacement'; +import { MSG } from './treePlacementProtocol'; +import { TREES } from '@/state/settings/components/trees'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; +import { ISLAND_GEOMETRY } from '@/state/settings/components/island'; +import { WORLD } from '@/state/settings/world/world'; import type { CityBbox, CityLayout } from '@/types'; interface PendingRequest { diff --git a/app/src/scene/components/trees/treePlacementWorker.ts b/app/src/scene/components/trees/treePlacementWorker.ts index e2b1f864..c6166847 100644 --- a/app/src/scene/components/trees/treePlacementWorker.ts +++ b/app/src/scene/components/trees/treePlacementWorker.ts @@ -4,12 +4,12 @@ // runs the scan, posts back the TreePlacement[]. Pure compute, no // DOM, no three.js references. -import { placeTrees, type TreePlacement } from './treePlacement.js'; -import { TREES } from '@/config/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; -import { WORLD } from '@/config/world/world.js'; -import type { IslandGeometryConfig } from '@/config/components/island.js'; +import { placeTrees, type TreePlacement } from './treePlacement'; +import { TREES } from '@/state/settings/components/trees'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; +import { WORLD } from '@/state/settings/world/world'; +import type { IslandGeometryConfig } from '@/state/settings/components/island'; import type { CityBbox, CityLayout } from '@/types'; type TreesValue = ReturnType; @@ -17,7 +17,7 @@ type BuildingDimsValue = ReturnType; type FootprintValue = ReturnType; type WorldValue = ReturnType; -import { MSG } from './treePlacementProtocol.js'; +import { MSG } from './treePlacementProtocol'; interface PlaceRequest { type: typeof MSG.REQUEST; diff --git a/app/src/scene/components/trees/treeRenderer.ts b/app/src/scene/components/trees/treeRenderer.ts index 225bc9a8..3929239b 100644 --- a/app/src/scene/components/trees/treeRenderer.ts +++ b/app/src/scene/components/trees/treeRenderer.ts @@ -23,9 +23,9 @@ // strength) goes through the rebuild path in hotReload.ts. import * as THREE from 'three'; -import { TREES } from '@/config/components/trees.js'; +import { TREES } from '@/state/settings/components/trees'; import { RENDER_ORDERS } from '@/constants'; -import type { TreePlacement } from './treePlacement.js'; +import type { TreePlacement } from './treePlacement'; import type { CommitEntry } from '@/types'; import { computeAgeRange, @@ -37,9 +37,9 @@ import { type AgeRange, type SizeRange, type DailyCounts, -} from './treeEncoding.js'; -import { interpolateOklch } from '@/scene/utils/color/colors.js'; -import { sunDirFromLighting } from '@/scene/components/lighting/sunDir.js'; +} from './treeEncoding'; +import { interpolateOklch } from '@/scene/utils/color/colors'; +import { sunDirFromLighting } from '@/scene/components/lighting/sunDir'; export interface Trees { group: THREE.Group; diff --git a/app/src/scene/components/trees/trees.ts b/app/src/scene/components/trees/trees.ts index f2d9b68a..c5dd93f0 100644 --- a/app/src/scene/components/trees/trees.ts +++ b/app/src/scene/components/trees/trees.ts @@ -10,8 +10,8 @@ // trees.refresh(); // on applyTheme() — called on Save // trees.dispose(); // on rebuild / scene teardown -import { createTreeRenderer, type Trees } from './treeRenderer.js'; -import type { TreePlacement } from './treePlacement.js'; +import { createTreeRenderer, type Trees } from './treeRenderer'; +import type { TreePlacement } from './treePlacement'; import type { CommitEntry } from '@/types'; export function createTrees(placements: TreePlacement[], commits: CommitEntry[] | null): Trees { diff --git a/app/src/scene/effects/buildingFader.ts b/app/src/scene/effects/buildingFader.ts index 27a5cc1a..c2af6f38 100644 --- a/app/src/scene/effects/buildingFader.ts +++ b/app/src/scene/effects/buildingFader.ts @@ -15,12 +15,12 @@ // The fader writes iFade on each CellTile.detailMesh (vec3 layout). import * as THREE from 'three'; -import { BUILDING_FADE } from '@/config/index.js'; +import { BUILDING_FADE } from '@/state/settings/index'; import { FadeDetail, NodeKind } from '@/types'; import type { DirNode, FileNode, PickTarget } from '@/types'; -import { parentDirPath } from '@/scene/utils/path.js'; -import type { createWorld } from '@/scene/world.js'; -import type { createPicker } from '@/scene/system/picker.js'; +import { parentDirPath } from '@/scene/utils/path'; +import type { createWorld } from '@/scene/world'; +import type { createPicker } from '@/scene/system/picker'; interface TierResult { detail: FadeDetail; diff --git a/app/src/scene/effects/ghostRenderer.ts b/app/src/scene/effects/ghostRenderer.ts index 759a27e2..1a672eee 100644 --- a/app/src/scene/effects/ghostRenderer.ts +++ b/app/src/scene/effects/ghostRenderer.ts @@ -22,8 +22,8 @@ import * as THREE from 'three'; import { NodeKind } from '@/types'; import { RENDER_ORDERS } from '@/constants'; -import type { createWorld } from '@/scene/world.js'; -import type { createPicker } from '@/scene/system/picker.js'; +import type { createWorld } from '@/scene/world'; +import type { createPicker } from '@/scene/system/picker'; import type { FileTarget } from '@/types'; // Opacity for the hover ghost overlay. Intentionally light so the ghost diff --git a/app/src/scene/effects/outlineRenderer.ts b/app/src/scene/effects/outlineRenderer.ts index f89b7a35..51ba1198 100644 --- a/app/src/scene/effects/outlineRenderer.ts +++ b/app/src/scene/effects/outlineRenderer.ts @@ -21,13 +21,13 @@ import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { BUILDING_OUTLINE, RAINBOW } from '@/config/index.js'; +import { BUILDING_OUTLINE, RAINBOW } from '@/state/settings/index'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind } from '@/types'; -import { UNIT_BOX_EDGE_POSITIONS } from '@/scene/world.js'; -import { getBuildingTilt } from '@/scene/components/buildings/buildingTilt.js'; -import type { createWorld } from '@/scene/world.js'; -import type { createPicker } from '@/scene/system/picker.js'; +import { UNIT_BOX_EDGE_POSITIONS } from '@/scene/world'; +import { getBuildingTilt } from '@/scene/components/buildings/buildingTilt'; +import type { createWorld } from '@/scene/world'; +import type { createPicker } from '@/scene/system/picker'; import type { FileTarget } from '@/types'; export function createOutlineRenderer({ diff --git a/app/src/scene/effects/pathLineRenderer.ts b/app/src/scene/effects/pathLineRenderer.ts index d8442cd7..34fc4931 100644 --- a/app/src/scene/effects/pathLineRenderer.ts +++ b/app/src/scene/effects/pathLineRenderer.ts @@ -13,7 +13,7 @@ import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { PATH_LINE, HOVER_PATH_LINE, RAINBOW, STREET_TIERS } from '@/config/index.js'; +import { PATH_LINE, HOVER_PATH_LINE, RAINBOW, STREET_TIERS } from '@/state/settings/index'; /** * Converts a LINEWIDTH_PCT percentage (1–50) into an actual pixel linewidth @@ -30,9 +30,9 @@ export function computePathLinewidthPixels(pct: number): number { } import { RENDER_ORDERS } from '@/constants'; import { NodeKind } from '@/types'; -import { computePathPoints } from '@/scene/utils/path.js'; -import type { createWorld } from '@/scene/world.js'; -import type { createPicker } from '@/scene/system/picker.js'; +import { computePathPoints } from '@/scene/utils/path'; +import type { createWorld } from '@/scene/world'; +import type { createPicker } from '@/scene/system/picker'; export function createPathLineRenderer({ canvas, diff --git a/app/src/scene/effects/treeOutlineRenderer.ts b/app/src/scene/effects/treeOutlineRenderer.ts index 87936e81..015abe4d 100644 --- a/app/src/scene/effects/treeOutlineRenderer.ts +++ b/app/src/scene/effects/treeOutlineRenderer.ts @@ -19,12 +19,12 @@ import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { TREE_OUTLINE } from '@/config/components/trees.js'; -import { RAINBOW } from '@/config/effects/effects.js'; +import { TREE_OUTLINE } from '@/state/settings/components/trees'; +import { RAINBOW } from '@/state/settings/effects/effects'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind } from '@/types'; -import { buildCanopyEdges } from '@/scene/components/trees/treeRenderer.js'; -import type { PickTarget } from '@/types/picker.js'; +import { buildCanopyEdges } from '@/scene/components/trees/treeRenderer'; +import type { PickTarget } from '@/types/picker'; import type { ReadableAtom } from 'nanostores'; interface TreesHandle { diff --git a/app/src/scene/layout/cellAssembly.ts b/app/src/scene/layout/cellAssembly.ts index 124f5358..17800a55 100644 --- a/app/src/scene/layout/cellAssembly.ts +++ b/app/src/scene/layout/cellAssembly.ts @@ -7,19 +7,19 @@ // the engine handles those fine even at Linux scale. import * as THREE from 'three'; -import { SpatialGrid, type WorldBounds } from './spatialGrid.js'; -import { createEmptyCellTile, type CellTile, allocateSlot } from './cellTile.js'; +import { SpatialGrid, type WorldBounds } from './spatialGrid'; +import { createEmptyCellTile, type CellTile, allocateSlot } from './cellTile'; import { attachBuildingMeshToCell, writeBuildingToSlot, -} from '../components/buildings/buildingsCell.js'; +} from '../components/buildings/buildingsCell'; import { InstancedAdPanels, asyncLoadMediaForBuilding, -} from '../components/adPanels/adPanelsInstanced.js'; -import { isMediaFile } from '../components/adPanels/adPanels.js'; -import { BuildingIndex } from '../components/buildings/buildingIndex.js'; -import type { Building } from '@/types/index.js'; +} from '../components/adPanels/adPanelsInstanced'; +import { isMediaFile } from '../components/adPanels/adPanels'; +import { BuildingIndex } from '../components/buildings/buildingIndex'; +import type { Building } from '@/types/index'; export interface CellAssemblyOutput { grid: SpatialGrid; diff --git a/app/src/scene/layout/cellTile.ts b/app/src/scene/layout/cellTile.ts index 21a3f8f1..32befb2e 100644 --- a/app/src/scene/layout/cellTile.ts +++ b/app/src/scene/layout/cellTile.ts @@ -9,9 +9,9 @@ // is just the data carrier and the empty constructor. import * as THREE from 'three'; -import type { Building } from '@/types/index.js'; -import type { DirNode } from '@/types/manifest.js'; -import type { SpatialGrid } from './spatialGrid.js'; +import type { Building } from '@/types/index'; +import type { DirNode } from '@/types/manifest'; +import type { SpatialGrid } from './spatialGrid'; export interface CellTile { cellId: number; diff --git a/app/src/scene/layout/layout.ts b/app/src/scene/layout/layout.ts index bdf89cc8..21a0969d 100644 --- a/app/src/scene/layout/layout.ts +++ b/app/src/scene/layout/layout.ts @@ -14,14 +14,19 @@ // querying a WorldOccupancy structure for the smallest stem offset that // keeps the new geometry from intersecting anything already placed. -import { STREET_TIERS, BUILDING_DIMENSIONS, STREET_LAYOUT, GEM_SIZING } from '@/config/index.js'; -import type { StreetTier } from '@/config/components/streets.js'; +import { + STREET_TIERS, + BUILDING_DIMENSIONS, + STREET_LAYOUT, + GEM_SIZING, +} from '@/state/settings/index'; +import type { StreetTier } from '@/state/settings/components/streets'; import { BuildingOrient, JoinSide, NodeKind, StreetAxis } from '@/types'; import type { Building, CityLayout, RangeStat, Street } from '@/types'; -import { parentDirPath } from '../utils/path.js'; -import { isMediaFile } from '../components/adPanels/adPanels.js'; -import { WorldOccupancy, WorldRectKind } from './worldOccupancy.js'; -import type { WorldRect } from './worldOccupancy.js'; +import { parentDirPath } from '../utils/path'; +import { isMediaFile } from '../components/adPanels/adPanels'; +import { WorldOccupancy, WorldRectKind } from './worldOccupancy'; +import type { WorldRect } from './worldOccupancy'; // Structural shapes — kept lenient so test fixtures (which omit fields the // helpers don't read, like name/path on intermediate nodes) stay diff --git a/app/src/scene/layout/layoutClient.ts b/app/src/scene/layout/layoutClient.ts index 839cecf1..b42295ec 100644 --- a/app/src/scene/layout/layoutClient.ts +++ b/app/src/scene/layout/layoutClient.ts @@ -15,8 +15,13 @@ // dimensions) is recomputed from the new manifest. This is the cheap // path for skeleton→final transitions and live updates. -import { STREET_LAYOUT, BUILDING_DIMENSIONS, GEM_SIZING, STREET_TIERS } from '@/config/index.js'; -import { layoutCity, makeHeightContext, recomputeBuildingDimensions } from './layout.js'; +import { + STREET_LAYOUT, + BUILDING_DIMENSIONS, + GEM_SIZING, + STREET_TIERS, +} from '@/state/settings/index'; +import { layoutCity, makeHeightContext, recomputeBuildingDimensions } from './layout'; import type { Manifest, CityLayout, FileNode, TreeNode } from '@/types'; interface PendingRequest { diff --git a/app/src/scene/layout/layoutWorker.ts b/app/src/scene/layout/layoutWorker.ts index 108c16fc..64b8bd4a 100644 --- a/app/src/scene/layout/layoutWorker.ts +++ b/app/src/scene/layout/layoutWorker.ts @@ -3,8 +3,13 @@ // the worker's local store instances, runs the layout, posts the // result back. Pure compute, no DOM or THREE.* references. -import { layoutCity } from './layout.js'; -import { STREET_LAYOUT, BUILDING_DIMENSIONS, GEM_SIZING, STREET_TIERS } from '@/config/index.js'; +import { layoutCity } from './layout'; +import { + STREET_LAYOUT, + BUILDING_DIMENSIONS, + GEM_SIZING, + STREET_TIERS, +} from '@/state/settings/index'; import type { Manifest } from '@/types'; import type { CityLayout } from '@/types'; diff --git a/app/src/scene/layout/worldBounds.ts b/app/src/scene/layout/worldBounds.ts index 2ea9d58e..15a624ff 100644 --- a/app/src/scene/layout/worldBounds.ts +++ b/app/src/scene/layout/worldBounds.ts @@ -17,7 +17,7 @@ // renders. import type { CityBbox } from '@/types'; -import { WORLD } from '@/config/world/world.js'; +import { WORLD } from '@/state/settings/world/world'; /** Fallback half-extent when no bbox is available (pre-layout, * non-git smoke tests). Keeps the floor visible at the origin. */ diff --git a/app/src/scene/renderLoop.ts b/app/src/scene/renderLoop.ts index 3ad4c6d2..278393cb 100644 --- a/app/src/scene/renderLoop.ts +++ b/app/src/scene/renderLoop.ts @@ -16,27 +16,27 @@ import { GEM_GLOW, GEM_SIZING, BLOOM, -} from '../config/index.js'; +} from '../state/settings/index'; import { NodeKind, StreetAxis } from '../types'; import type { Manifest } from '../types'; -import { SKY } from '@/config/components/sky.js'; -import { createWorld } from './world.js'; -import { refreshBuildingMaterial } from './components/buildings/buildings.js'; -import { createCameraRig } from './system/cameraRig.js'; -import { createAnimator } from './system/animator.js'; -import { createPicker } from './system/picker.js'; -import { createInputHandlers } from './system/inputHandlers.js'; -import { createBuildingFader } from './effects/buildingFader.js'; -import { createOutlineRenderer } from './effects/outlineRenderer.js'; -import { createTreeOutlineRenderer } from './effects/treeOutlineRenderer.js'; -import { createGhostRenderer } from './effects/ghostRenderer.js'; -import { createPathLineRenderer } from './effects/pathLineRenderer.js'; -import { createCoordinator } from '../coordinator.js'; -import { showTooltip, hideTooltip } from '../views/widgets/tooltip.js'; -import { createPostFx } from './system/postFx.js'; -import { registerRenderer as registerAdPanelRenderer } from './components/adPanels/adPanelTextureArray.js'; -import { labelFromManifest } from '../views/widgets/displayLabel.js'; +import { SKY } from '@/state/settings/components/sky'; +import { createWorld } from './world'; +import { refreshBuildingMaterial } from './components/buildings/buildings'; +import { createCameraRig } from './system/cameraRig'; +import { createAnimator } from './system/animator'; +import { createPicker } from './system/picker'; +import { createInputHandlers } from './system/inputHandlers'; +import { createBuildingFader } from './effects/buildingFader'; +import { createOutlineRenderer } from './effects/outlineRenderer'; +import { createTreeOutlineRenderer } from './effects/treeOutlineRenderer'; +import { createGhostRenderer } from './effects/ghostRenderer'; +import { createPathLineRenderer } from './effects/pathLineRenderer'; +import { createCoordinator } from '../coordinator'; +import { showTooltip, hideTooltip } from '../views/components/tooltip'; +import { createPostFx } from './system/postFx'; +import { registerRenderer as registerAdPanelRenderer } from './components/adPanels/adPanelTextureArray'; +import { labelFromManifest } from '@/utils/sources'; // Rewrite manifest.tree.name to the friendly label derived from display_root // so that every downstream consumer (root street label, file tree root row, diff --git a/app/src/scene/system/animator.ts b/app/src/scene/system/animator.ts index 69b8f735..6990827a 100644 --- a/app/src/scene/system/animator.ts +++ b/app/src/scene/system/animator.ts @@ -28,9 +28,9 @@ // cannot conflict by construction. import * as THREE from 'three'; -import { ANIMATION_TIMING } from '@/config/index.js'; +import { ANIMATION_TIMING } from '@/state/settings/index'; import type { Building, WorldDiff } from '@/types'; -import type { createWorld } from '../world.js'; +import type { createWorld } from '../world'; interface Tween { /** Slot index within the CellTile InstancedMesh (same as building.slotId). */ diff --git a/app/src/scene/system/cameraRig.ts b/app/src/scene/system/cameraRig.ts index ff380aa0..128454eb 100644 --- a/app/src/scene/system/cameraRig.ts +++ b/app/src/scene/system/cameraRig.ts @@ -26,11 +26,11 @@ import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; -import { CAMERA_PERSPECTIVE, CAMERA_CONTROLS, ANIMATION_TIMING } from '@/config/index.js'; -import { CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; +import { CAMERA_PERSPECTIVE, CAMERA_CONTROLS, ANIMATION_TIMING } from '@/state/settings/index'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext'; import { StreetAxis } from '@/types'; import type { Building, Street } from '@/types'; -import type { createWorld } from '../world.js'; +import type { createWorld } from '../world'; /** Floor on controls.maxDistance regardless of city size. Tiny-but-tall * cities (small footprint, one big building) end up with a tiny diff --git a/app/src/scene/system/inputHandlers.ts b/app/src/scene/system/inputHandlers.ts index 03a85b63..567f0cee 100644 --- a/app/src/scene/system/inputHandlers.ts +++ b/app/src/scene/system/inputHandlers.ts @@ -5,18 +5,18 @@ // const handlers = createInputHandlers({ // canvas, picker, rig, renderer, camera, // onResize: function () { /* renderer-specific onResize work */ }, -// showTooltip, hideTooltip, // tooltip api (from views/widgets/tooltip.js) +// showTooltip, hideTooltip, // tooltip api (from views/components/tooltip.js) // }); // handlers.dispose(); import * as THREE from 'three'; -import { INPUT_TIMING } from '@/config/index.js'; +import { INPUT_TIMING } from '@/state/settings/index'; import { KEY_BINDINGS, TEXT_INPUT_TAGS } from '@/constants'; import { NodeKind } from '@/types'; import type { PickTarget } from '@/types'; -import { formatRelativeAge } from '@/utils/dates.js'; -import type { createPicker } from './picker.js'; -import type { createCameraRig } from './cameraRig.js'; +import { formatRelativeAge } from '@/utils/dates'; +import type { createPicker } from './picker'; +import type { createCameraRig } from './cameraRig'; export function createInputHandlers({ canvas, diff --git a/app/src/scene/system/postFx.ts b/app/src/scene/system/postFx.ts index d737b757..4b20637a 100644 --- a/app/src/scene/system/postFx.ts +++ b/app/src/scene/system/postFx.ts @@ -23,7 +23,7 @@ import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'; import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'; import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'; -import { BLOOM } from '@/config/index.js'; +import { BLOOM } from '@/state/settings/index'; export interface PostFx { render(): void; diff --git a/app/src/scene/utils/color/registerShaderChunks.ts b/app/src/scene/utils/color/registerShaderChunks.ts index 930998e8..5faa0d1b 100644 --- a/app/src/scene/utils/color/registerShaderChunks.ts +++ b/app/src/scene/utils/color/registerShaderChunks.ts @@ -6,7 +6,7 @@ import * as THREE from 'three'; import hslGlslSrc from './hsl.glsl?raw'; -import { FOG_UNIFORMS_GLSL, FOG_APPLY_GLSL } from '@/scene/components/lighting/fogChunk.js'; +import { FOG_UNIFORMS_GLSL, FOG_APPLY_GLSL } from '@/scene/components/lighting/fogChunk'; let _registered = false; diff --git a/app/src/scene/world.ts b/app/src/scene/world.ts index 77a1c2bb..68baf172 100644 --- a/app/src/scene/world.ts +++ b/app/src/scene/world.ts @@ -31,45 +31,45 @@ import * as THREE from 'three'; import { LineSegments2 } from 'three/addons/lines/LineSegments2.js'; import { LineMaterial } from 'three/addons/lines/LineMaterial.js'; -import { registerShaderChunks } from './utils/color/registerShaderChunks.js'; -import { getSharedBuildingUniforms } from './components/buildings/buildings.js'; -import { disposeLabelMaterials } from './components/labels/labels.js'; -import { buildCellsFromLayout } from './layout/cellAssembly.js'; -import type { CellTile } from './layout/cellTile.js'; -import { BuildingIndex } from './components/buildings/buildingIndex.js'; -import { findLayoutOverlaps } from './layout/layout.js'; -import type { LayoutOverlap } from './layout/layout.js'; -import { createLayoutClient } from './layout/layoutClient.js'; -import type { LayoutComputeOpts } from './layout/layoutClient.js'; -import { layoutCityWithTrace } from './layout/layout.js'; -import type { ChildPlacementTrace, StemPlacementTrace } from './layout/layout.js'; -import type { WorldRect } from './layout/worldOccupancy.js'; -import { createRootGem } from './components/gem/gem.js'; -import { createStreetMesh } from './components/streets/streets.js'; -import { createStreetLabels } from './components/streets/streetLabels.js'; -import { createSky } from './components/sky/sky.js'; -import type { Sky } from './components/sky/sky.js'; -import { createRepoLabel } from './components/repoLabel/repoLabel.js'; -import type { RepoLabel } from './components/repoLabel/repoLabel.js'; -import { createTrees } from './components/trees/trees.js'; -import type { Trees } from './components/trees/trees.js'; -import { createFireflies } from './components/fireflies/fireflies.js'; -import type { Fireflies } from './components/fireflies/fireflies.js'; -import { createTreePlacementClient } from './components/trees/treePlacementClient.js'; -import type { TreePlacementClient } from './components/trees/treePlacementClient.js'; -import { createIsland } from './components/island/islandMesh.js'; -import type { Island } from './components/island/islandMesh.js'; -import { getWorldBounds, type WorldBounds } from './layout/worldBounds.js'; -import { createCityFootprint } from './components/footprint/footprint.js'; -import type { CityFootprint } from './components/footprint/footprint.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; +import { registerShaderChunks } from './utils/color/registerShaderChunks'; +import { getSharedBuildingUniforms } from './components/buildings/buildings'; +import { disposeLabelMaterials } from './components/labels/labels'; +import { buildCellsFromLayout } from './layout/cellAssembly'; +import type { CellTile } from './layout/cellTile'; +import { BuildingIndex } from './components/buildings/buildingIndex'; +import { findLayoutOverlaps } from './layout/layout'; +import type { LayoutOverlap } from './layout/layout'; +import { createLayoutClient } from './layout/layoutClient'; +import type { LayoutComputeOpts } from './layout/layoutClient'; +import { layoutCityWithTrace } from './layout/layout'; +import type { ChildPlacementTrace, StemPlacementTrace } from './layout/layout'; +import type { WorldRect } from './layout/worldOccupancy'; +import { createRootGem } from './components/gem/gem'; +import { createStreetMesh } from './components/streets/streets'; +import { createStreetLabels } from './components/streets/streetLabels'; +import { createSky } from './components/sky/sky'; +import type { Sky } from './components/sky/sky'; +import { createRepoLabel } from './components/repoLabel/repoLabel'; +import type { RepoLabel } from './components/repoLabel/repoLabel'; +import { createTrees } from './components/trees/trees'; +import type { Trees } from './components/trees/trees'; +import { createFireflies } from './components/fireflies/fireflies'; +import type { Fireflies } from './components/fireflies/fireflies'; +import { createTreePlacementClient } from './components/trees/treePlacementClient'; +import type { TreePlacementClient } from './components/trees/treePlacementClient'; +import { createIsland } from './components/island/islandMesh'; +import type { Island } from './components/island/islandMesh'; +import { getWorldBounds, type WorldBounds } from './layout/worldBounds'; +import { createCityFootprint } from './components/footprint/footprint'; +import type { CityFootprint } from './components/footprint/footprint'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; import { getBuildingColor, getCreatedAge, getModifiedAge, getDateRanges, -} from './components/buildings/buildingColor.js'; -import { SKY } from '@/config/components/sky.js'; +} from './components/buildings/buildingColor'; +import { SKY } from '@/state/settings/components/sky'; import { ASPHALT, GEM_APPEARANCE, @@ -80,8 +80,8 @@ import { TREES, SCENE_COLORS, SIDEWALK_COLORS, -} from '@/config/index.js'; -import { REBUILD_STATUS } from '@/store/liveStatus.js'; +} from '@/state/settings/index'; +import { REBUILD_STATUS } from '@/state/runtime/liveStatus'; import type { Building, CityBbox, diff --git a/app/src/store/configDrafts.ts b/app/src/state/drafts.ts similarity index 97% rename from app/src/store/configDrafts.ts rename to app/src/state/drafts.ts index a05129e0..92291cf6 100644 --- a/app/src/store/configDrafts.ts +++ b/app/src/state/drafts.ts @@ -1,4 +1,4 @@ -// config/drafts.ts — in-memory draft layer between the controls panel +// state/drafts.ts — in-memory draft layer between the controls panel // and the real nanostores. Widgets read getEffective() and write // setDraft(); the Save button calls commit() to flush every draft into // its store (which triggers the existing persist + commit-reaction @@ -9,7 +9,7 @@ // Storage shape: Map>. For atom stores // (no setKey), key = null and the inner map has at most one entry. -import { forEachRegisteredStore, getDefault } from './persist.js'; +import { forEachRegisteredStore, getDefault } from './persist'; interface MapLikeStore { get(): any; diff --git a/app/src/store/persist.ts b/app/src/state/persist.ts similarity index 98% rename from app/src/store/persist.ts rename to app/src/state/persist.ts index 75b25fad..d6d991ca 100644 --- a/app/src/store/persist.ts +++ b/app/src/state/persist.ts @@ -1,4 +1,4 @@ -// config/persist.ts — Mirrors every config store to localStorage so the +// state/persist.ts — Mirrors every settings store to localStorage so the // Settings UI's tweaks survive a page reload. localStorage holds ONLY values // that differ from the original defaults, so a fresh / cleared install starts // with no entries at all and resetting a value back to its default removes @@ -17,7 +17,7 @@ import type { WritableAtom } from 'nanostores'; import { STORAGE_PREFIX, STORAGE_PER_SOURCE_PREFIX } from '@/constants'; -import { CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext'; // Defaults snapshotted at attach time, BEFORE hydration. These are what the // "reset to default" UI restores to and what the diff-vs-default check uses. diff --git a/app/src/store/configCommitReactions.ts b/app/src/state/reactions.ts similarity index 98% rename from app/src/store/configCommitReactions.ts rename to app/src/state/reactions.ts index e01fc1c2..a64ba97a 100644 --- a/app/src/store/configCommitReactions.ts +++ b/app/src/state/reactions.ts @@ -1,4 +1,4 @@ -// store/configCommitReactions.ts — every config store classified as either +// state/reactions.ts — every settings store classified as either // "rebuild-required" or "material-only", and wired to the matching // reaction that fires once when the user clicks Save in the Controls pane. // @@ -18,7 +18,7 @@ import { listenKeys } from 'nanostores'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from '@/store/liveStatus.js'; +import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from '@/state/runtime/liveStatus'; import { // Rebuild-required (affects layout or geometry): @@ -71,19 +71,19 @@ import { // Cyberpunk Valley — floating repo-name label (all keys material-only // via repoLabel.refresh() inside applyTheme()): REPO_LABEL, -} from '@/config/index.js'; +} from '@/state/settings/index'; import { // Cyberpunk Valley — island geometry and materials // (all material-only via island.refresh() inside applyTheme()): ISLAND_GEOMETRY, ISLAND_MATERIALS, -} from '@/config/components/island.js'; +} from '@/state/settings/components/island'; import { // Cyberpunk Valley — fireflies (structural: visibility is determined at // creation time by reading FIREFLIES_ENABLED, so any change requires a // full rebuild — there is no refresh() hot-path): FIREFLIES, -} from '@/config/components/fireflies.js'; +} from '@/state/settings/components/fireflies'; // Min-dwell for the 'rebuilding' indicator on the material-only path. // applyTheme() is synchronous and finishes within microseconds, so without diff --git a/app/src/store/liveStatus.ts b/app/src/state/runtime/liveStatus.ts similarity index 88% rename from app/src/store/liveStatus.ts rename to app/src/state/runtime/liveStatus.ts index f8e22866..4fc820cc 100644 --- a/app/src/store/liveStatus.ts +++ b/app/src/state/runtime/liveStatus.ts @@ -1,14 +1,14 @@ -// liveStatus.ts — transient runtime state for the world-rebuild signal. -// Lives OUTSIDE app/config/ on purpose: these atoms are session-only +// state/runtime/liveStatus.ts — transient runtime state for the world-rebuild signal. +// Lives OUTSIDE state/settings/ on purpose: these atoms are session-only // and must NOT be persisted to localStorage. If REBUILD_STATUS were -// re-exported from the config barrel, attachPersistence(Config) would +// re-exported from the settings barrel, attachPersistence(Config) would // rehydrate `'rebuilding'` from a session that ended mid-fetch — and // the poll's status-gated logic would then strand the footer on // "rebuilding…" forever. // // Two writers, both feed the same atoms: // - setupLiveUpdates() in main.ts — live-poll fetch + applyManifest -// - scheduleRebuild() in config/hotReload.ts — save-driven applyManifest +// - scheduleRebuild() in state/settings/hotReload.ts — save-driven applyManifest // // LAST_UPDATED_AT is written by the coordinator on every applied // manifest (initial paint + each successful poll that swapped state). diff --git a/app/src/store/liveUpdates.ts b/app/src/state/runtime/liveUpdates.ts similarity index 95% rename from app/src/store/liveUpdates.ts rename to app/src/state/runtime/liveUpdates.ts index ee422399..a6ecb7b3 100644 --- a/app/src/store/liveUpdates.ts +++ b/app/src/state/runtime/liveUpdates.ts @@ -1,4 +1,4 @@ -// liveUpdates.ts — Live-update poll loop. When LIVE_UPDATES.ENABLED flips +// state/runtime/liveUpdates.ts — Live-update poll loop. When LIVE_UPDATES.ENABLED flips // on we start re-fetching the manifest at the user-configured interval; // when its signature changes vs. the last render, we hand the new manifest // to world.applyManifest, which rebuilds the city in place. Camera + @@ -13,11 +13,10 @@ // only flipped during the actual manifest fetch so the footer's // "rebuilding…" indicator only lights up when there's real work. -import { LIVE_UPDATES, POLL_SECONDS_MIN, POLL_SECONDS_MAX } from '@/config/index.js'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, setRefreshManifest } from '@/store/liveStatus.js'; -import { streamManifest } from '@/utils/manifestStream.js'; -import { manifestUrl, signatureUrl } from '@/utils/url.js'; -import { _applyDisplayLabel, startRenderLoop } from '@/scene/renderLoop.js'; +import { LIVE_UPDATES, POLL_SECONDS_MIN, POLL_SECONDS_MAX } from '@/state/settings/index'; +import { REBUILD_STATUS, LAST_REBUILD_ERROR, setRefreshManifest } from '@/state/runtime/liveStatus'; +import { manifestUrl, signatureUrl, streamManifest } from '@/api/manifest'; +import { _applyDisplayLabel, startRenderLoop } from '@/scene/renderLoop'; function _clampPollSeconds(s: number | unknown): number { if (typeof s !== 'number' || !isFinite(s)) return POLL_SECONDS_MIN; diff --git a/app/src/views/source/localFlag.ts b/app/src/state/runtime/localFlag.ts similarity index 95% rename from app/src/views/source/localFlag.ts rename to app/src/state/runtime/localFlag.ts index 6ccdd337..e51e2a2d 100644 --- a/app/src/views/source/localFlag.ts +++ b/app/src/state/runtime/localFlag.ts @@ -1,4 +1,4 @@ -// views/source/localFlag.ts — boolean flags persisted in localStorage. +// state/runtime/localFlag.ts — boolean flags persisted in localStorage. // // Storage convention: write '1' for on, REMOVE the key for off (so a // fresh / cleared install starts with every flag at its default and diff --git a/app/src/store/sourceContext.ts b/app/src/state/runtime/sourceContext.ts similarity index 92% rename from app/src/store/sourceContext.ts rename to app/src/state/runtime/sourceContext.ts index 305d4383..86d9e4f8 100644 --- a/app/src/store/sourceContext.ts +++ b/app/src/state/runtime/sourceContext.ts @@ -1,4 +1,4 @@ -// sourceContext.ts — Stable short hash of a source identity (src + branch), +// state/runtime/sourceContext.ts — Stable short hash of a source identity (src + branch), // plus a single nanostore atom that tracks which source is currently // loaded. Other modules (selection persistence, camera-pose persistence) // subscribe to CURRENT_SOURCE_KEY to swap their localStorage slots when diff --git a/app/src/views/source/sourceRecents.ts b/app/src/state/runtime/sourceRecents.ts similarity index 93% rename from app/src/views/source/sourceRecents.ts rename to app/src/state/runtime/sourceRecents.ts index 22cbee09..a22ec432 100644 --- a/app/src/views/source/sourceRecents.ts +++ b/app/src/state/runtime/sourceRecents.ts @@ -1,5 +1,5 @@ -// sourceRecents.ts — Tiny localStorage-backed list of recently-opened -// sources. Used by the source picker modal to show one-click reload rows. +// state/runtime/sourceRecents.ts — Tiny localStorage-backed list of recently- +// opened sources. Used by the source picker modal to show one-click reload rows. const KEY = 'codecity:recents'; const MAX = 10; diff --git a/app/src/config/components/adPanels.ts b/app/src/state/settings/components/adPanels.ts similarity index 92% rename from app/src/config/components/adPanels.ts rename to app/src/state/settings/components/adPanels.ts index b88ce2f0..b0d94409 100644 --- a/app/src/config/components/adPanels.ts +++ b/app/src/state/settings/components/adPanels.ts @@ -1,4 +1,4 @@ -// config/adPanel.ts — Procedural ad-panel geometry + colors. Ad panels +// state/settings/adPanel.ts — Procedural ad-panel geometry + colors. Ad panels // are the textured planes mounted on the front face of media-file // buildings (image / video). Changes require a full rebuild because // the mesh geometry is baked at manifest-apply time (createAdPanel diff --git a/app/src/config/components/buildings.ts b/app/src/state/settings/components/buildings.ts similarity index 98% rename from app/src/config/components/buildings.ts rename to app/src/state/settings/components/buildings.ts index 86f9f91c..f278a656 100644 --- a/app/src/config/components/buildings.ts +++ b/app/src/state/settings/components/buildings.ts @@ -1,4 +1,4 @@ -// config/building.js — Everything visual about a building: dimensions, +// state/settings/building.js — Everything visual about a building: dimensions, // palette, outline, and selection-driven fade tiers. // // DIMENSIONS + PALETTE changes are rebuild-required (regenerate per-building @@ -64,7 +64,7 @@ export const BUILDING_PALETTE = map({ // getLightness fall back to the midpoint of the range above so the // building reads as "average" rather than crushed to either extreme.) // Hue is picked to match the standout color of the file's icon in - // Material Icon Theme (see views/widgets/fileIcon.ts) — the COLOR you + // Material Icon Theme (see views/components/fileIcon.ts) — the COLOR you // see on the file's pill / icon glyph is the same hue the building // ramps off in the city. When an icon has multiple colors, we pick // the more distinctive one (e.g. Python is yellow+blue → yellow, diff --git a/app/src/config/components/facade.ts b/app/src/state/settings/components/facade.ts similarity index 98% rename from app/src/config/components/facade.ts rename to app/src/state/settings/components/facade.ts index d528ab73..f6a30345 100644 --- a/app/src/config/components/facade.ts +++ b/app/src/state/settings/components/facade.ts @@ -1,4 +1,4 @@ -// config/facade.ts — Procedural facade rendering controls. Three stores +// state/settings/facade.ts — Procedural facade rendering controls. Three stores // cover everything the building shader uses to render a wall: // FACADE_GEOMETRY — floor / window / door / roof-border sizes, // plus the per-building window column count. diff --git a/app/src/config/components/fireflies.ts b/app/src/state/settings/components/fireflies.ts similarity index 97% rename from app/src/config/components/fireflies.ts rename to app/src/state/settings/components/fireflies.ts index a6053060..c405347e 100644 --- a/app/src/config/components/fireflies.ts +++ b/app/src/state/settings/components/fireflies.ts @@ -1,4 +1,4 @@ -// config/fireflies.ts — committer-fireflies tunables. +// state/settings/fireflies.ts — committer-fireflies tunables. // // v2 exposes the size + animation knobs. BOB is the y-axis sinusoid // the shader applies to displace each orb; PULSE is a brightness diff --git a/app/src/config/components/footprint.ts b/app/src/state/settings/components/footprint.ts similarity index 95% rename from app/src/config/components/footprint.ts rename to app/src/state/settings/components/footprint.ts index fc791080..e31b251b 100644 --- a/app/src/config/components/footprint.ts +++ b/app/src/state/settings/components/footprint.ts @@ -1,4 +1,4 @@ -// config/footprint.ts — Cyberpunk Valley city footprint configuration. +// state/settings/footprint.ts — Cyberpunk Valley city footprint configuration. // // One InstancedMesh per layout rect (buildings + streets + paths), // each scaled up by HALO_WIDTH world units in both axes, painted with diff --git a/app/src/config/components/gem.ts b/app/src/state/settings/components/gem.ts similarity index 97% rename from app/src/config/components/gem.ts rename to app/src/state/settings/components/gem.ts index 0cc24cfe..e30921f1 100644 --- a/app/src/config/components/gem.ts +++ b/app/src/state/settings/components/gem.ts @@ -1,9 +1,9 @@ -// config/gem.js — Root-of-repo gem: sizing (rebuild-required), face palette +// state/settings/gem.js — Root-of-repo gem: sizing (rebuild-required), face palette // (applied on Save via vertex color buffer rewrite), edge color (applied on Save via applyTheme()), // and animation tuning (applied on Save via applyTheme(); read fresh per frame). import { map } from 'nanostores'; -import { oklchToHex } from '@/scene/utils/color/colors.js'; +import { oklchToHex } from '@/scene/utils/color/colors'; // ─── Sizing + landing zone ──────────────────────────────────────────────── // Layout reserves dead space around the gem based on these — changing any diff --git a/app/src/config/components/island.ts b/app/src/state/settings/components/island.ts similarity index 95% rename from app/src/config/components/island.ts rename to app/src/state/settings/components/island.ts index 676bb0d7..c5bf7c68 100644 --- a/app/src/config/components/island.ts +++ b/app/src/state/settings/components/island.ts @@ -1,4 +1,4 @@ -// config/island.ts — Floating-island world-plane configuration. +// state/settings/island.ts — Floating-island world-plane configuration. // // Replaces the visual fields previously in WORLD (GROUND_COLOR, // GROUND_ENABLED, ENABLED). Sizing config (GROUND_BUFFER_PERCENT) diff --git a/app/src/config/components/lighting.ts b/app/src/state/settings/components/lighting.ts similarity index 95% rename from app/src/config/components/lighting.ts rename to app/src/state/settings/components/lighting.ts index 68b398de..f6360ca6 100644 --- a/app/src/config/components/lighting.ts +++ b/app/src/state/settings/components/lighting.ts @@ -1,4 +1,4 @@ -// config/lighting.ts — Scene-level directional lighting controls. +// state/settings/lighting.ts — Scene-level directional lighting controls. // Replaces hardcoded SUN_DIR_WORLD / AMBIENT / DIFFUSE_GAIN consts in the // building fragment shader. Persisted via attachPersistence(Config) at boot // and wired through refreshBuildingMaterial() on every change. diff --git a/app/src/config/components/repoLabel.ts b/app/src/state/settings/components/repoLabel.ts similarity index 96% rename from app/src/config/components/repoLabel.ts rename to app/src/state/settings/components/repoLabel.ts index b075ccae..50f62008 100644 --- a/app/src/config/components/repoLabel.ts +++ b/app/src/state/settings/components/repoLabel.ts @@ -1,4 +1,4 @@ -// config/components/repoLabel.ts — Floating repo-name label configuration. +// state/settings/components/repoLabel.ts — Floating repo-name label configuration. // One nanostore drives the createRepoLabel() factory's transform uniforms // via applyTheme() on Save — see configCommitReactions.ts. // diff --git a/app/src/config/components/sky.ts b/app/src/state/settings/components/sky.ts similarity index 94% rename from app/src/config/components/sky.ts rename to app/src/state/settings/components/sky.ts index 7092861a..69882cc0 100644 --- a/app/src/config/components/sky.ts +++ b/app/src/state/settings/components/sky.ts @@ -1,4 +1,4 @@ -// config/sky.ts — Cyberpunk Valley sky configuration. Two nanostore +// state/settings/sky.ts — Cyberpunk Valley sky configuration. Two nanostore // map()s drive the procedural sky shader's uniforms via the // `applyTheme()` path — applied on Save (see configCommitReactions.ts). // diff --git a/app/src/config/components/streets.ts b/app/src/state/settings/components/streets.ts similarity index 98% rename from app/src/config/components/streets.ts rename to app/src/state/settings/components/streets.ts index b6e7d4af..d7f5895f 100644 --- a/app/src/config/components/streets.ts +++ b/app/src/state/settings/components/streets.ts @@ -1,4 +1,4 @@ -// config/street.js — Everything visual + layout-y about a street: asphalt, +// state/settings/street.js — Everything visual + layout-y about a street: asphalt, // sidewalks, road labels, the neon path line, and how streets are sized + // packed (tiers + gaps). Asphalt color + sidewalk variants are applied on Save // via applyTheme(); label typography + tiers + gaps are rebuild-required. diff --git a/app/src/config/components/trees.ts b/app/src/state/settings/components/trees.ts similarity index 98% rename from app/src/config/components/trees.ts rename to app/src/state/settings/components/trees.ts index e7ba90d9..741e5db8 100644 --- a/app/src/config/components/trees.ts +++ b/app/src/state/settings/components/trees.ts @@ -1,4 +1,4 @@ -// config/trees.ts — Commit-driven tree configuration. +// state/settings/trees.ts — Commit-driven tree configuration. // // One tree per commit. Trees are scattered around the world floor with // a density falloff (denser near the city, sparser far away), sorted diff --git a/app/src/config/effects/effects.ts b/app/src/state/settings/effects/effects.ts similarity index 97% rename from app/src/config/effects/effects.ts rename to app/src/state/settings/effects/effects.ts index fdf7f139..47cd9361 100644 --- a/app/src/config/effects/effects.ts +++ b/app/src/state/settings/effects/effects.ts @@ -1,4 +1,4 @@ -// config/effects.js — Cross-cutting visual effects shared between multiple +// state/settings/effects.js — Cross-cutting visual effects shared between multiple // consumers. Keeping these in one place means tweaking the look (e.g. "make // all rainbows slower") doesn't require chasing the same values through // multiple per-target stores. diff --git a/app/src/config/index.ts b/app/src/state/settings/index.ts similarity index 72% rename from app/src/config/index.ts rename to app/src/state/settings/index.ts index 553d74b2..9605799f 100644 --- a/app/src/config/index.ts +++ b/app/src/state/settings/index.ts @@ -1,4 +1,4 @@ -// config/index.ts — Single source of truth for every user-tunable atom. +// state/settings/index.ts — Single source of truth for every user-tunable atom. // // Configs are grouped on disk by where they're consumed, with file // names mirroring the consumer: @@ -34,31 +34,31 @@ // see src/constants/ or inlined private consts in their consumer. // ── System (camera, input, animation, tooltip) ─────────────────────── -export * from './system/cameraRig.js'; -export * from './system/animator.js'; -export * from './system/inputHandlers.js'; -export * from './system/tooltip.js'; +export * from './system/cameraRig'; +export * from './system/animator'; +export * from './system/inputHandlers'; +export * from './system/tooltip'; // ── App-level user preferences ──────────────────────────────────────── -export * from './prefs/liveUpdates.js'; -export * from './prefs/syntaxTheme.js'; +export * from './prefs/liveUpdates'; +export * from './prefs/syntaxTheme'; // ── World (background + sizing) ─────────────────────────────────────── -export * from './world/world.js'; +export * from './world/world'; // ── Visual components ───────────────────────────────────────────────── -export * from './components/sky.js'; -export * from './components/lighting.js'; -export * from './components/streets.js'; -export * from './components/buildings.js'; -export * from './components/facade.js'; -export * from './components/adPanels.js'; -export * from './components/gem.js'; -export * from './components/island.js'; -export * from './components/footprint.js'; -export * from './components/trees.js'; -export * from './components/fireflies.js'; -export * from './components/repoLabel.js'; +export * from './components/sky'; +export * from './components/lighting'; +export * from './components/streets'; +export * from './components/buildings'; +export * from './components/facade'; +export * from './components/adPanels'; +export * from './components/gem'; +export * from './components/island'; +export * from './components/footprint'; +export * from './components/trees'; +export * from './components/fireflies'; +export * from './components/repoLabel'; // ── Cross-cutting visual effects ────────────────────────────────────── -export * from './effects/effects.js'; +export * from './effects/effects'; diff --git a/app/src/config/prefs/liveUpdates.ts b/app/src/state/settings/prefs/liveUpdates.ts similarity index 94% rename from app/src/config/prefs/liveUpdates.ts rename to app/src/state/settings/prefs/liveUpdates.ts index d8c61206..729fe6aa 100644 --- a/app/src/config/prefs/liveUpdates.ts +++ b/app/src/state/settings/prefs/liveUpdates.ts @@ -1,4 +1,4 @@ -// config/live.js — Live-update polling. When ENABLED, the frontend +// state/settings/live.js — Live-update polling. When ENABLED, the frontend // re-fetches /api/manifest every POLL_SECONDS and reloads the page when // the manifest's signature changes — i.e. the underlying tree's mtime // or size state shifted. diff --git a/app/src/config/prefs/syntaxTheme.ts b/app/src/state/settings/prefs/syntaxTheme.ts similarity index 96% rename from app/src/config/prefs/syntaxTheme.ts rename to app/src/state/settings/prefs/syntaxTheme.ts index 7b8f252b..bd08e661 100644 --- a/app/src/config/prefs/syntaxTheme.ts +++ b/app/src/state/settings/prefs/syntaxTheme.ts @@ -1,4 +1,4 @@ -// config/syntaxTheme.ts — User-selected highlight.js syntax theme. +// state/settings/syntaxTheme.ts — User-selected highlight.js syntax theme. // Subscribed in main.ts to swap a element's href // so the CSS theme applies immediately without a page reload or re-render. // Persisted via attachPersistence (Config barrel) so the choice survives diff --git a/app/src/config/system/animator.ts b/app/src/state/settings/system/animator.ts similarity index 94% rename from app/src/config/system/animator.ts rename to app/src/state/settings/system/animator.ts index b98f02a6..50c0fe3b 100644 --- a/app/src/config/system/animator.ts +++ b/app/src/state/settings/system/animator.ts @@ -1,4 +1,4 @@ -// config/animation.ts — Shared timing primitives for camera + building +// state/settings/animation.ts — Shared timing primitives for camera + building // transitions. Replaces the per-action absolute durations that previously // lived in CAMERA_ANIMATION and the hard-coded ENTER_MS / STAY_MS in // animator.ts. diff --git a/app/src/config/system/cameraRig.ts b/app/src/state/settings/system/cameraRig.ts similarity index 97% rename from app/src/config/system/cameraRig.ts rename to app/src/state/settings/system/cameraRig.ts index fbdb7ccf..f69e449c 100644 --- a/app/src/config/system/cameraRig.ts +++ b/app/src/state/settings/system/cameraRig.ts @@ -1,4 +1,4 @@ -// config/system/cameraRig.ts — Camera lens, orbit/zoom controls, and +// state/settings/system/cameraRig.ts — Camera lens, orbit/zoom controls, and // per-action focus framing. Read by scene/system/cameraRig.ts at scene // init (perspective + controls — rebuild-required) and per-gesture // (animation — applied on Save via applyTheme() — see configCommitReactions.ts). diff --git a/app/src/config/system/inputHandlers.ts b/app/src/state/settings/system/inputHandlers.ts similarity index 88% rename from app/src/config/system/inputHandlers.ts rename to app/src/state/settings/system/inputHandlers.ts index 8285a481..3bd8aa12 100644 --- a/app/src/config/system/inputHandlers.ts +++ b/app/src/state/settings/system/inputHandlers.ts @@ -1,4 +1,4 @@ -// config/system/inputHandlers.ts — Pointer input timing. Click vs drag +// state/settings/system/inputHandlers.ts — Pointer input timing. Click vs drag // detection and hover stickiness. Consumed by // scene/system/inputHandlers.ts. Read per-event so the Settings UI's // tweaks apply immediately. diff --git a/app/src/config/system/tooltip.ts b/app/src/state/settings/system/tooltip.ts similarity index 72% rename from app/src/config/system/tooltip.ts rename to app/src/state/settings/system/tooltip.ts index 77fb59fb..b35203af 100644 --- a/app/src/config/system/tooltip.ts +++ b/app/src/state/settings/system/tooltip.ts @@ -1,5 +1,5 @@ -// config/system/tooltip.ts — Tooltip placement. Consumed by -// views/widgets/tooltip.ts. +// state/settings/system/tooltip.ts — Tooltip placement. Consumed by +// views/components/tooltip.ts. import { map } from 'nanostores'; diff --git a/app/src/config/world/world.ts b/app/src/state/settings/world/world.ts similarity index 96% rename from app/src/config/world/world.ts rename to app/src/state/settings/world/world.ts index ea6c7819..d6977eb1 100644 --- a/app/src/config/world/world.ts +++ b/app/src/state/settings/world/world.ts @@ -1,4 +1,4 @@ -// config/world/world.ts — World sizing + background. Scene-level color +// state/settings/world/world.ts — World sizing + background. Scene-level color // concerns (background, atmospheric fog) live here; per-component // visuals (island materials, sky color, etc.) live in their own // component config. diff --git a/app/src/styles.css b/app/src/styles.css index 90d116a1..2e7922e3 100644 --- a/app/src/styles.css +++ b/app/src/styles.css @@ -741,7 +741,7 @@ h6 { instead of being collapsed to currentColor. Same 1em sizing as .lucide-icon so it slots into header buttons without throwing off alignment. The .lucide-icon mask path is the simple monochrome variant; - see makeGemIcon() in views/widgets/icon.ts. */ + see makeGemIcon() in views/components/icon.ts. */ .gem-icon { display: inline-block; width: 1em; diff --git a/app/src/types/picker.ts b/app/src/types/picker.ts index d3c5cb70..0b6d0b79 100644 --- a/app/src/types/picker.ts +++ b/app/src/types/picker.ts @@ -5,8 +5,8 @@ import type * as THREE from 'three'; import type { Building } from './building'; import type { Street } from './street'; import type { CommitEntry, DirNode, FileNode, NodeKind } from './manifest'; -import type { BuildingIndex } from '@/scene/components/buildings/buildingIndex.js'; -import type { CellTile } from '@/scene/layout/cellTile.js'; +import type { BuildingIndex } from '@/scene/components/buildings/buildingIndex'; +import type { CellTile } from '@/scene/layout/cellTile'; /** Hovered/selected file (a building mesh). */ export interface FileTarget { diff --git a/app/src/utils/commit.ts b/app/src/utils/commit.ts new file mode 100644 index 00000000..c2566de1 --- /dev/null +++ b/app/src/utils/commit.ts @@ -0,0 +1,25 @@ +// utils/commit.ts — Pure helpers for per-commit UI concerns: building +// a browseable commit URL from the scanner's normalized remote, and +// counting same-day commits for the busyness badge. Used by the commit +// pane and the coordinator. + +import type { CommitEntry } from '@/types'; + +/** + * Build a browseable commit URL from a normalized remote URL + full SHA. + * + * Uses the `/commit/{sha}` suffix that GitHub, GitLab, Bitbucket, Gitea, + * Codeberg, and Forgejo all share. Hosts with a different convention + * will 404, which is acceptable — the link is best-effort and the SHA + * is always shown in plain text as the fallback. + */ +export function commitUrl(remote: string, sha: string): string | null { + if (!remote || !sha) return null; + const trimmed = remote.endsWith('/') ? remote.slice(0, -1) : remote; + return `${trimmed}/commit/${sha}`; +} + +/** Count of commits whose date matches `commit.date` (includes `commit` itself). */ +export function sameDayCommitCount(commit: CommitEntry, commits: CommitEntry[]): number { + return commits.filter((c) => c.date === commit.date).length; +} diff --git a/app/src/utils/source.ts b/app/src/utils/source.ts deleted file mode 100644 index 7acdbbac..00000000 --- a/app/src/utils/source.ts +++ /dev/null @@ -1,19 +0,0 @@ -// source.ts — Source URL classification helpers. Used by the boot -// orchestrator and the source-picker flow to render the loading overlay -// and the recents list with a kind ('git' | 'local') and a short label. - -export function _srcKind(src: string): 'git' | 'local' { - return /:\/\//.test(src) || /^[^@]+@[^:]+:/.test(src) ? 'git' : 'local'; -} - -export function _deriveLabel(src: string): string { - if (_srcKind(src) === 'git') { - // git URL — try "owner/repo" from the last two path segments - const m = src.match(/[/:]([^/]+)\/([^/]+?)(?:\.git)?$/); - if (m) return `${m[1]}/${m[2]}`; - return src; - } - // Local path — basename - const parts = src.split(/[/\\]/).filter(Boolean); - return parts.length ? parts[parts.length - 1] : src; -} diff --git a/app/src/views/widgets/displayLabel.ts b/app/src/utils/sources.ts similarity index 75% rename from app/src/views/widgets/displayLabel.ts rename to app/src/utils/sources.ts index 3b586d34..7bcc18ed 100644 --- a/app/src/views/widgets/displayLabel.ts +++ b/app/src/utils/sources.ts @@ -1,20 +1,23 @@ -// views/widgets/displayLabel.ts — Shared helpers for deriving a short, -// human-friendly project label. +// utils/sources.ts — Source URL helpers: classification (git vs local), +// canonicalisation (SSH → HTTPS), and label derivation (URL/path → human +// label, manifest → label). // -// Two entry points: -// - labelFromUrl — pure URL/path → label transform. Used by -// applyPendingTitle in main.ts, where we have a -// bare display_root string from the first stream -// event and NO manifest yet. -// - labelFromManifest — manifest-aware: prefers display_root, falls -// back to repo.remote_url, then tree.name. -// -// Exported as a standalone module so coordinator.ts, main.ts, renderLoop.ts, -// and any future callers share one implementation instead of duplicating -// the URL/path parsing logic. +// Public surface: +// - srcKind(src) — 'git' | 'local' discriminator. +// - toHttpsRepoUrl(src) — canonical https URL from any repo URL form. +// - labelFromUrl(src) — pure URL/path → "owner/repo" or basename. +// - labelFromManifest(m) — manifest-aware: display_root, remote_url, +// or tree.name fallback. import type { Manifest } from '@/types/manifest'; +/** Classify a source string as a git URL or a local path. Git URLs are + * recognised by either a scheme (https://, ssh://, etc.) or the + * scp-style `user@host:path` form. Anything else is local. */ +export function srcKind(src: string): 'git' | 'local' { + return /:\/\//.test(src) || /^[^@]+@[^:]+:/.test(src) ? 'git' : 'local'; +} + /** * Convert any recognisable repo URL form to an https:// URL. * https://… / http://… → returned as-is. diff --git a/app/src/views/widgets/badge.ts b/app/src/views/components/badge.ts similarity index 98% rename from app/src/views/widgets/badge.ts rename to app/src/views/components/badge.ts index 5132f50e..d28c38dd 100644 --- a/app/src/views/widgets/badge.ts +++ b/app/src/views/components/badge.ts @@ -1,4 +1,4 @@ -// views/widgets/badge.ts — Shared builder for the small pill that shows +// views/components/badge.ts — Shared builder for the small pill that shows // "what kind of thing am I looking at": a file extension (color-coded // from the same hue palette the city uses) or a generic "dir" badge // (painted with the asphalt color). Used by both the floating header @@ -11,7 +11,7 @@ // light text on dark backgrounds. That keeps the label readable no // matter what colors the user picks in Controls. -import { getHue } from '@/scene/components/buildings/buildingColor.js'; +import { getHue } from '@/scene/components/buildings/buildingColor'; const TEXT_DARK = '#0a0b10'; const TEXT_LIGHT = '#f4f6ff'; diff --git a/app/src/views/widgets/fileIcon.ts b/app/src/views/components/fileIcon.ts similarity index 99% rename from app/src/views/widgets/fileIcon.ts rename to app/src/views/components/fileIcon.ts index 4bc21413..2da1841c 100644 --- a/app/src/views/widgets/fileIcon.ts +++ b/app/src/views/components/fileIcon.ts @@ -1,4 +1,4 @@ -// views/widgets/fileIcon.ts — VSCode-style file/folder icons (Material +// views/components/fileIcon.ts — VSCode-style file/folder icons (Material // Icon Theme, MIT). Returns an element pointing at a CDN-hosted // SVG; the browser caches per URL so a project with N files but only K // unique extensions only triggers K icon fetches on first paint. diff --git a/app/src/views/widgets/icon.ts b/app/src/views/components/icon.ts similarity index 96% rename from app/src/views/widgets/icon.ts rename to app/src/views/components/icon.ts index 8ad7981e..e98ee517 100644 --- a/app/src/views/widgets/icon.ts +++ b/app/src/views/components/icon.ts @@ -1,4 +1,4 @@ -// views/widgets/icon.ts — Tiny helpers for inline icons. +// views/components/icon.ts — Tiny helpers for inline icons. // // makeLucideIcon — generic Lucide icon painted via CSS mask so it picks // up currentColor (used by every monochrome glyph). diff --git a/app/src/views/source/loadingOverlay.ts b/app/src/views/components/loadingOverlay.ts similarity index 98% rename from app/src/views/source/loadingOverlay.ts rename to app/src/views/components/loadingOverlay.ts index 4a953963..2c4bed3d 100644 --- a/app/src/views/source/loadingOverlay.ts +++ b/app/src/views/components/loadingOverlay.ts @@ -1,4 +1,4 @@ -// loadingOverlay.ts — Centered spinner + stepped progress indicator shown +// views/components/loadingOverlay.ts — Centered spinner + stepped progress indicator shown // whenever a manifest is being fetched or applied. Reused for direct-boot // loads and modal submits so the user sees the same UI regardless of entry // point. diff --git a/app/src/views/shell/paneHeader.ts b/app/src/views/components/paneHeader.ts similarity index 98% rename from app/src/views/shell/paneHeader.ts rename to app/src/views/components/paneHeader.ts index a05c3844..a6ef7296 100644 --- a/app/src/views/shell/paneHeader.ts +++ b/app/src/views/components/paneHeader.ts @@ -4,7 +4,7 @@ // `.text-pane-title` + `.pane-header-close` triplet, so the panes look // identical and adding a new pane is a one-call affair. -import { makeLucideIcon } from '../widgets/icon.js'; +import { makeLucideIcon } from '../components/icon'; interface BuildPaneHeaderOpts { /** Initial title text. Update later via the returned api.setTitle. */ diff --git a/app/src/views/widgets/pathTruncate.ts b/app/src/views/components/pathTruncate.ts similarity index 98% rename from app/src/views/widgets/pathTruncate.ts rename to app/src/views/components/pathTruncate.ts index 7185bdbc..5a65ff18 100644 --- a/app/src/views/widgets/pathTruncate.ts +++ b/app/src/views/components/pathTruncate.ts @@ -1,4 +1,4 @@ -// views/widgets/pathTruncate.ts — middle-truncation algorithm for path breadcrumbs. +// views/components/pathTruncate.ts — middle-truncation algorithm for path breadcrumbs. // // Exported by the module so both appHeader.ts and filePreviewPane.ts can share // the same logic without duplication. diff --git a/app/src/views/source/sourcePicker.ts b/app/src/views/components/sourcePicker.ts similarity index 98% rename from app/src/views/source/sourcePicker.ts rename to app/src/views/components/sourcePicker.ts index 4acd709e..9b81cb96 100644 --- a/app/src/views/source/sourcePicker.ts +++ b/app/src/views/components/sourcePicker.ts @@ -1,8 +1,8 @@ -// sourcePicker.ts — Modal for picking the active source (local path or +// views/components/sourcePicker.ts — Modal for picking the active source (local path or // git URL). Owns its own DOM, mounted into #source-picker-root. Recents // are pulled from sourceRecents.ts each time the modal renders. -import { listRecents, removeRecent } from './sourceRecents.js'; +import { listRecents, removeRecent } from '@/state/runtime/sourceRecents'; import { LUCIDE_ICON_BASE_URL } from '@/constants'; // ── Hosting-site SVG icons ─────────────────────────────────────────────────── diff --git a/app/src/views/widgets/tooltip.ts b/app/src/views/components/tooltip.ts similarity index 92% rename from app/src/views/widgets/tooltip.ts rename to app/src/views/components/tooltip.ts index 681a3f5d..922773ff 100644 --- a/app/src/views/widgets/tooltip.ts +++ b/app/src/views/components/tooltip.ts @@ -1,10 +1,10 @@ -// views/widgets/tooltip.js — Tiny floating label that follows the cursor +// views/components/tooltip.js — Tiny floating label that follows the cursor // on hover. Shown when the user is hovering a building/street; hidden // otherwise. Inspired by Cities: Skylines / SimCity — every interactive // object has a brief name label so the city feels alive without forcing // a sidebar open. -import { TOOLTIP } from '@/config/index.js'; +import { TOOLTIP } from '@/state/settings/index'; import { DOM_IDS } from '@/constants'; let _el: HTMLElement | null = null; diff --git a/app/src/views/panes/commitPane.ts b/app/src/views/panes/commitPane.ts index cf2c91e6..7280fd7d 100644 --- a/app/src/views/panes/commitPane.ts +++ b/app/src/views/panes/commitPane.ts @@ -15,12 +15,12 @@ // sidebar without churn. import type { CommitEntry } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { commitUrl } from './commitUrl.js'; -import { formatRelativeAge, formatFullDate } from '@/utils/dates.js'; -import { fetchCommitDetail } from './commitFetch.js'; -import { colorForAuthor } from '@/scene/components/fireflies/authorColor.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; +import { commitUrl } from '@/utils/commit'; +import { formatRelativeAge, formatFullDate } from '@/utils/dates'; +import { fetchCommitDetail } from '@/api/commit'; +import { colorForAuthor } from '@/scene/components/fireflies/authorColor'; interface BuildCommitPaneOpts { onClose?: () => void; diff --git a/app/src/views/panes/commitUrl.ts b/app/src/views/panes/commitUrl.ts deleted file mode 100644 index 28384cc2..00000000 --- a/app/src/views/panes/commitUrl.ts +++ /dev/null @@ -1,12 +0,0 @@ -// views/panes/commitUrl.ts — build a browseable commit URL from the -// normalized remote URL the scanner emits + a full SHA. Uses the -// `/commit/{sha}` suffix that GitHub, GitLab, Bitbucket, Gitea, -// Codeberg, and Forgejo all share. Hosts with a different convention -// will 404, which is acceptable — the link is best-effort and the SHA -// is always shown in plain text as the fallback. - -export function commitUrl(remote: string, sha: string): string | null { - if (!remote || !sha) return null; - const trimmed = remote.endsWith('/') ? remote.slice(0, -1) : remote; - return `${trimmed}/commit/${sha}`; -} diff --git a/app/src/views/panes/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 31a20d0a..f2ab45e3 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -47,18 +47,22 @@ import { SYNTAX_THEME, SYNTAX_THEME_DEFAULT, SYNTAX_THEME_OPTIONS, -} from '@/config/index.js'; -import { SKY, SKY_STARS } from '@/config/components/sky.js'; -import { REPO_LABEL } from '@/config/components/repoLabel.js'; -import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/config/components/island.js'; -import { WORLD } from '@/config/world/world.js'; -import { TREES, TREE_OUTLINE } from '@/config/components/trees.js'; -import { FIREFLIES } from '@/config/components/fireflies.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; -import { FACADE_GEOMETRY, FACADE_DETAIL, WINDOW_LIGHTING } from '@/config/components/facade.js'; -import { AD_PANEL } from '@/config/components/adPanels.js'; -import { ANIMATION_TIMING } from '@/config/system/animator.js'; -import { getDefault, forEachRegisteredStore, onAnyChange } from '@/store/persist.js'; +} from '@/state/settings/index'; +import { SKY, SKY_STARS } from '@/state/settings/components/sky'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; +import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island'; +import { WORLD } from '@/state/settings/world/world'; +import { TREES, TREE_OUTLINE } from '@/state/settings/components/trees'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; +import { + FACADE_GEOMETRY, + FACADE_DETAIL, + WINDOW_LIGHTING, +} from '@/state/settings/components/facade'; +import { AD_PANEL } from '@/state/settings/components/adPanels'; +import { ANIMATION_TIMING } from '@/state/settings/system/animator'; +import { getDefault, forEachRegisteredStore, onAnyChange } from '@/state/persist'; import { setDraft, getEffective, @@ -68,11 +72,11 @@ import { discard as discardDrafts, isDirty as draftsAreDirty, subscribe as subscribeDrafts, -} from '@/store/configDrafts.js'; +} from '@/state/drafts'; import { KEY_BINDINGS } from '@/constants'; import { FadeDetail } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; // Structural store shape used by all the widget builders. Covers nanostores // `map()` (with .setKey) and falls back to .set for atom-like stores. diff --git a/app/src/views/panes/filePreviewPane.ts b/app/src/views/panes/filePreviewPane.ts index 7795f336..b6329d7b 100644 --- a/app/src/views/panes/filePreviewPane.ts +++ b/app/src/views/panes/filePreviewPane.ts @@ -10,12 +10,12 @@ // views/shell/rightSidebar.js. import hljs from 'highlight.js/lib/common'; -import { ASPHALT, BUILDING_PALETTE } from '@/config'; +import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; import { PreviewKind } from '@/types'; import type { FileNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/widgets/badge.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; +import { makeExtensionBadge } from '@/views/components/badge'; // Binary-unit thresholds for human-readable file size formatting. const BYTES_PER_KB = 1024; diff --git a/app/src/views/panes/infoPane.ts b/app/src/views/panes/infoPane.ts index 36b16f41..5d529eac 100644 --- a/app/src/views/panes/infoPane.ts +++ b/app/src/views/panes/infoPane.ts @@ -7,8 +7,8 @@ import { marked } from 'marked'; import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; // Match README, README.md, readme.markdown, README.txt — any file whose // stem (case-insensitive) is "readme". GitHub/VSCode use the same rule. diff --git a/app/src/views/panes/searchPane.ts b/app/src/views/panes/searchPane.ts index 6ce7f6e2..0d0a8e0d 100644 --- a/app/src/views/panes/searchPane.ts +++ b/app/src/views/panes/searchPane.ts @@ -17,8 +17,8 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; const MAX_RESULTS = 50; const WORD_BOUNDARY_RE = /[/_\-.]/; diff --git a/app/src/views/panes/streetPane.ts b/app/src/views/panes/streetPane.ts index 7c6a6578..8ae3e85b 100644 --- a/app/src/views/panes/streetPane.ts +++ b/app/src/views/panes/streetPane.ts @@ -9,10 +9,10 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/widgets/badge.js'; -import { ASPHALT, BUILDING_PALETTE } from '@/config'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; +import { makeExtensionBadge } from '@/views/components/badge'; +import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; interface BuildStreetPaneOpts { onClose?: () => void; diff --git a/app/src/views/panes/treePane.ts b/app/src/views/panes/treePane.ts index 2cff50b3..4b19d121 100644 --- a/app/src/views/panes/treePane.ts +++ b/app/src/views/panes/treePane.ts @@ -5,9 +5,9 @@ import { NodeKind } from '@/types'; import type { DirNode, Manifest, TreeNode } from '@/types'; -import { makeGemIcon, makeLucideIcon } from '@/views/widgets/icon.js'; -import { makeFileIcon, makeFolderIcon } from '@/views/widgets/fileIcon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeGemIcon, makeLucideIcon } from '@/views/components/icon'; +import { makeFileIcon, makeFolderIcon } from '@/views/components/fileIcon'; +import { buildPaneHeader } from '@/views/components/paneHeader'; interface TreeCtx { byPath: Record; diff --git a/app/src/views/shell/appFooter.ts b/app/src/views/shell/appFooter.ts index 3d171042..1153e49a 100644 --- a/app/src/views/shell/appFooter.ts +++ b/app/src/views/shell/appFooter.ts @@ -15,7 +15,7 @@ // The refresh/reset-view button has moved to the header (far right). import { DateSource, NodeKind } from '@/types'; -import { formatShortDate, formatRelativeAgeShort } from '@/utils/dates.js'; +import { formatShortDate, formatRelativeAgeShort } from '@/utils/dates'; interface FooterFileSelection { kind: NodeKind.File; diff --git a/app/src/views/shell/appHeader.ts b/app/src/views/shell/appHeader.ts index 2e5c3a78..96e96077 100644 --- a/app/src/views/shell/appHeader.ts +++ b/app/src/views/shell/appHeader.ts @@ -6,11 +6,11 @@ // // When no path is selected (or only the root), #app-title is empty. -import { ASPHALT, BUILDING_PALETTE } from '@/config'; -import { makeGemIcon, makeLucideIcon } from '../widgets/icon.js'; -import { makeExtensionBadge } from '../widgets/badge.js'; -import { toHttpsRepoUrl } from '../widgets/displayLabel.js'; -import { fitSegments } from '../widgets/pathTruncate.js'; +import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; +import { makeGemIcon, makeLucideIcon } from '../components/icon'; +import { makeExtensionBadge } from '../components/badge'; +import { toHttpsRepoUrl } from '@/utils/sources'; +import { fitSegments } from '../components/pathTruncate'; // How long the "Copied!" badge lingers after the copy button is clicked. const COPY_FEEDBACK_DURATION_MS = 1500; diff --git a/app/src/views/shell/leftSidebar.ts b/app/src/views/shell/leftSidebar.ts index 67a8c6a3..29936a77 100644 --- a/app/src/views/shell/leftSidebar.ts +++ b/app/src/views/shell/leftSidebar.ts @@ -5,14 +5,14 @@ // in the panel header also collapses; persisted in localStorage) // - drag-to-resize handle on the sidebar's right edge (also persisted) -import { buildTreePane } from '@/views/panes/treePane.js'; -import { buildInfoPane } from '@/views/panes/infoPane.js'; -import { buildControlsPane } from '@/views/panes/controlsPane.js'; -import { buildSearchPane } from '@/views/panes/searchPane.js'; +import { buildTreePane } from '@/views/panes/treePane'; +import { buildInfoPane } from '@/views/panes/infoPane'; +import { buildControlsPane } from '@/views/panes/controlsPane'; +import { buildSearchPane } from '@/views/panes/searchPane'; import { ACTIVITY_BAR_TABS, DOM_IDS, LUCIDE_ICON_BASE_URL, STORAGE_KEYS } from '@/constants'; import { SidebarTab } from '@/types'; import type { DirNode, Manifest, TreeNode } from '@/types'; -import { loadFlag, saveFlag } from '../source/localFlag.js'; +import { loadFlag, saveFlag } from '@/state/runtime/localFlag'; const SIDEBAR_MIN_WIDTH = 280; const SIDEBAR_MAX_WIDTH = 600; diff --git a/app/src/views/widgets/commitMetrics.ts b/app/src/views/widgets/commitMetrics.ts deleted file mode 100644 index 65cc623d..00000000 --- a/app/src/views/widgets/commitMetrics.ts +++ /dev/null @@ -1,9 +0,0 @@ -// views/widgets/commitMetrics.ts — pure helpers for per-commit UI metrics -// used by the commit pane and (eventually) other views. - -import type { CommitEntry } from '@/types'; - -/** Count of commits whose date matches `commit.date` (includes `commit` itself). */ -export function sameDayCommitCount(commit: CommitEntry, commits: CommitEntry[]): number { - return commits.filter((c) => c.date === commit.date).length; -} diff --git a/app/tests/_helpers/cityFixtures.ts b/app/tests/_helpers/cityFixtures.ts index 8c9c6587..c22a9d83 100644 --- a/app/tests/_helpers/cityFixtures.ts +++ b/app/tests/_helpers/cityFixtures.ts @@ -31,8 +31,8 @@ import { NodeKind } from '@/types'; import type { CityBbox, CityLayout } from '@/types'; -import { TREES } from '@/config/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; +import { TREES } from '@/state/settings/components/trees'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; /** Builds a CityBbox from extents, deriving cx/cy/width/depth. */ export function bbox(minX: number, minY: number, maxX: number, maxY: number): CityBbox { diff --git a/app/tests/_helpers/layoutAsserts.ts b/app/tests/_helpers/layoutAsserts.ts index da8dee9c..742cdc0d 100644 --- a/app/tests/_helpers/layoutAsserts.ts +++ b/app/tests/_helpers/layoutAsserts.ts @@ -2,8 +2,8 @@ // Moved here because layoutPacker.test.ts also needs them — and importing // from a .test.ts file forces vitest to register the source file's // describe() blocks twice across workers. -import { __test } from '@/scene/layout/layout.js'; -import type { Rect } from '@/scene/layout/layout.js'; +import { __test } from '@/scene/layout/layout'; +import type { Rect } from '@/scene/layout/layout'; import { StreetAxis } from '@/types'; import type { CityLayout, Street, Building } from '@/types'; diff --git a/app/tests/_helpers/typography.ts b/app/tests/_helpers/typography.ts index 55cc826c..eccd17de 100644 --- a/app/tests/_helpers/typography.ts +++ b/app/tests/_helpers/typography.ts @@ -10,7 +10,7 @@ // - cell tests place a mesh in the world and assert on Y, so 0.5. // Each suite spreads this and overrides ELEVATION as needed. -import type { LabelTypographyConfig } from '@/config/index.js'; +import type { LabelTypographyConfig } from '@/state/settings/index'; export const TYPOGRAPHY: LabelTypographyConfig = { FONT_FAMILY: 'sans-serif', diff --git a/app/tests/utils/serverConfig.test.ts b/app/tests/api/config.test.ts similarity index 94% rename from app/tests/utils/serverConfig.test.ts rename to app/tests/api/config.test.ts index 2982b288..e7da1278 100644 --- a/app/tests/utils/serverConfig.test.ts +++ b/app/tests/api/config.test.ts @@ -1,9 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { - fetchServerConfig, - getServerConfig, - _resetServerConfigForTests, -} from '@/utils/serverConfig.js'; +import { fetchServerConfig, getServerConfig, _resetServerConfigForTests } from '@/api/config'; describe('fetchServerConfig', () => { beforeEach(() => { diff --git a/app/tests/url.test.ts b/app/tests/api/index.test.ts similarity index 96% rename from app/tests/url.test.ts rename to app/tests/api/index.test.ts index 92494c73..6450d791 100644 --- a/app/tests/url.test.ts +++ b/app/tests/api/index.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { buildApiUrl } from '@/utils/url.js'; +import { buildApiUrl } from '@/api'; describe('buildApiUrl', () => { it('forwards src param when present', () => { diff --git a/app/tests/manifestStream.test.ts b/app/tests/api/manifest.test.ts similarity index 97% rename from app/tests/manifestStream.test.ts rename to app/tests/api/manifest.test.ts index 3432b13e..1dbe6f38 100644 --- a/app/tests/manifestStream.test.ts +++ b/app/tests/api/manifest.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { streamManifest } from '@/utils/manifestStream'; +import { streamManifest } from '@/api/manifest'; function mockResponse( chunks: string[], diff --git a/app/tests/scene/cityFootprint/footprint.test.ts b/app/tests/scene/cityFootprint/footprint.test.ts index db2b45a2..491675da 100644 --- a/app/tests/scene/cityFootprint/footprint.test.ts +++ b/app/tests/scene/cityFootprint/footprint.test.ts @@ -1,8 +1,8 @@ // app/tests/scene/cityFootprint/footprint.test.ts import { describe, it, expect, beforeEach } from 'vitest'; import * as THREE from 'three'; -import { createCityFootprint } from '@/scene/components/footprint/footprint.js'; -import { FOOTPRINT } from '@/config/components/footprint.js'; +import { createCityFootprint } from '@/scene/components/footprint/footprint'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; import { StreetAxis } from '@/types'; import type { CityLayout } from '@/types'; diff --git a/app/tests/scene/cityScene-collision.test.ts b/app/tests/scene/cityScene-collision.test.ts index b7e700e0..aff7a847 100644 --- a/app/tests/scene/cityScene-collision.test.ts +++ b/app/tests/scene/cityScene-collision.test.ts @@ -1,11 +1,11 @@ // world-collision.test.ts — _formatCollisionReport() partitions overlaps -import { WorldRectKind } from '@/scene/layout/worldOccupancy.js'; +import { WorldRectKind } from '@/scene/layout/worldOccupancy'; // into unexpected vs. t-junction, produces an info-level summary when clean, // and a warn-level summary + per-overlap detail block when dirty. import { describe, it, expect } from 'vitest'; -import { __test } from '@/scene/world.js'; -import type { LayoutOverlap } from '@/scene/layout/layout.js'; +import { __test } from '@/scene/world'; +import type { LayoutOverlap } from '@/scene/layout/layout'; const { _formatCollisionReport } = __test; diff --git a/app/tests/scene/cityScene-stem-diagnostic.test.ts b/app/tests/scene/cityScene-stem-diagnostic.test.ts index 2a242d45..19680ec2 100644 --- a/app/tests/scene/cityScene-stem-diagnostic.test.ts +++ b/app/tests/scene/cityScene-stem-diagnostic.test.ts @@ -1,14 +1,10 @@ // world-stem-diagnostic.test.ts — _formatStemDiagnostic groups child -import { WorldRectKind } from '@/scene/layout/worldOccupancy.js'; +import { WorldRectKind } from '@/scene/layout/worldOccupancy'; // placements by parent road and emits a multi-line block per parent. import { describe, it, expect } from 'vitest'; -import { __test } from '@/scene/world.js'; -import type { - StemPlacementTrace, - ChildPlacementTrace, - VariantTrace, -} from '@/scene/layout/layout.js'; +import { __test } from '@/scene/world'; +import type { StemPlacementTrace, ChildPlacementTrace, VariantTrace } from '@/scene/layout/layout'; const { _formatStemDiagnostic } = __test; diff --git a/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts b/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts index 7e649f9a..4c035e2e 100644 --- a/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts +++ b/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts @@ -3,14 +3,14 @@ import { describe, it, expect } from 'vitest'; import type * as THREE from 'three'; -import { InstancedAdPanels } from '@/scene/components/adPanels/adPanelsInstanced.js'; +import { InstancedAdPanels } from '@/scene/components/adPanels/adPanelsInstanced'; import { AdPanelTextureArray, PANEL_TEX_SIZE, -} from '@/scene/components/adPanels/adPanelTextureArray.js'; -import { BLOOM } from '@/config/index.js'; -import { BuildingOrient, NodeKind } from '@/types/index.js'; -import type { Building } from '@/types/index.js'; +} from '@/scene/components/adPanels/adPanelTextureArray'; +import { BLOOM } from '@/state/settings/index'; +import { BuildingOrient, NodeKind } from '@/types/index'; +import type { Building } from '@/types/index'; // --------------------------------------------------------------------------- // Minimal Building fixture — only the fields read by registerMediaBuilding. diff --git a/app/tests/scene/components/buildings/buildingColor.test.ts b/app/tests/scene/components/buildings/buildingColor.test.ts index d2609491..a82b6053 100644 --- a/app/tests/scene/components/buildings/buildingColor.test.ts +++ b/app/tests/scene/components/buildings/buildingColor.test.ts @@ -6,9 +6,9 @@ import { getDateRanges, getBuildingColor, getModifiedAge, -} from '@/scene/components/buildings/buildingColor.js'; -import { BUILDING_PALETTE } from '@/config/index.js'; -import type { BuildingPaletteConfig } from '@/config/components/buildings.js'; +} from '@/scene/components/buildings/buildingColor'; +import { BUILDING_PALETTE } from '@/state/settings/index'; +import type { BuildingPaletteConfig } from '@/state/settings/components/buildings'; import { NodeKind } from '@/types'; import type { RangeStat } from '@/types'; diff --git a/app/tests/scene/components/buildings/buildingIndex.test.ts b/app/tests/scene/components/buildings/buildingIndex.test.ts index d06266c1..2df5ab43 100644 --- a/app/tests/scene/components/buildings/buildingIndex.test.ts +++ b/app/tests/scene/components/buildings/buildingIndex.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; -import { BuildingIndex } from '@/scene/components/buildings/buildingIndex.js'; -import { NodeKind } from '@/types/index.js'; -import type { FileNode, DirNode } from '@/types/manifest.js'; +import { BuildingIndex } from '@/scene/components/buildings/buildingIndex'; +import { NodeKind } from '@/types/index'; +import type { FileNode, DirNode } from '@/types/manifest'; import { building } from '../../../_helpers/buildingFixture'; // Build a FileNode whose only meaningful fields are `path` and `name` (which diff --git a/app/tests/scene/components/buildings/instanced-buildings-cell.test.ts b/app/tests/scene/components/buildings/instanced-buildings-cell.test.ts index 218624fb..d5a79170 100644 --- a/app/tests/scene/components/buildings/instanced-buildings-cell.test.ts +++ b/app/tests/scene/components/buildings/instanced-buildings-cell.test.ts @@ -3,15 +3,15 @@ import { describe, it, expect, afterEach } from 'vitest'; import * as THREE from 'three'; -import { SpatialGrid } from '@/scene/layout/spatialGrid.js'; -import { createEmptyCellTile } from '@/scene/layout/cellTile.js'; +import { SpatialGrid } from '@/scene/layout/spatialGrid'; +import { createEmptyCellTile } from '@/scene/layout/cellTile'; import { attachBuildingMeshToCell, writeBuildingToSlot, setCellIconAtlas, -} from '@/scene/components/buildings/buildingsCell.js'; -import { BuildingOrient } from '@/types/index.js'; -import type { IconAtlas } from '@/scene/components/buildings/iconAtlas.js'; +} from '@/scene/components/buildings/buildingsCell'; +import { BuildingOrient } from '@/types/index'; +import type { IconAtlas } from '@/scene/components/buildings/iconAtlas'; import { building } from '../../../_helpers/buildingFixture'; // --------------------------------------------------------------------------- diff --git a/app/tests/scene/components/labels/instanced-labels-atlas.test.ts b/app/tests/scene/components/labels/instanced-labels-atlas.test.ts index 2a216fcb..51a523d1 100644 --- a/app/tests/scene/components/labels/instanced-labels-atlas.test.ts +++ b/app/tests/scene/components/labels/instanced-labels-atlas.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { buildLabelAtlas } from '@/scene/components/labels/labels.js'; -import { TYPOGRAPHY } from '../../../_helpers/typography.js'; +import { buildLabelAtlas } from '@/scene/components/labels/labels'; +import { TYPOGRAPHY } from '../../../_helpers/typography'; describe('buildLabelAtlas', () => { it('returns at least one page + UV rect per text', () => { diff --git a/app/tests/scene/components/labels/instanced-labels-cell.test.ts b/app/tests/scene/components/labels/instanced-labels-cell.test.ts index 519ad083..baf5c749 100644 --- a/app/tests/scene/components/labels/instanced-labels-cell.test.ts +++ b/app/tests/scene/components/labels/instanced-labels-cell.test.ts @@ -1,11 +1,11 @@ import { describe, it, expect, vi } from 'vitest'; import * as THREE from 'three'; -import { LABEL_TYPOGRAPHY } from '@/config/index.js'; -import { buildLabelAtlas } from '@/scene/components/labels/labelAtlas.js'; -import { attachLabelMeshToCell, writeLabelToSlot } from '@/scene/components/labels/labelsCell.js'; -import type { CellTile } from '@/scene/layout/cellTile.js'; -import { NodeKind } from '@/types/index.js'; -import { TYPOGRAPHY as BASE_TYPOGRAPHY } from '../../../_helpers/typography.js'; +import { LABEL_TYPOGRAPHY } from '@/state/settings/index'; +import { buildLabelAtlas } from '@/scene/components/labels/labelAtlas'; +import { attachLabelMeshToCell, writeLabelToSlot } from '@/scene/components/labels/labelsCell'; +import type { CellTile } from '@/scene/layout/cellTile'; +import { NodeKind } from '@/types/index'; +import { TYPOGRAPHY as BASE_TYPOGRAPHY } from '../../../_helpers/typography'; import { building } from '../../../_helpers/buildingFixture'; // --------------------------------------------------------------------------- diff --git a/app/tests/scene/components/repoLabel/repoLabel.test.ts b/app/tests/scene/components/repoLabel/repoLabel.test.ts index c88b6248..1f2209e3 100644 --- a/app/tests/scene/components/repoLabel/repoLabel.test.ts +++ b/app/tests/scene/components/repoLabel/repoLabel.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as THREE from 'three'; -import { createRepoLabel } from '@/scene/components/repoLabel/repoLabel.js'; -import { REPO_LABEL } from '@/config/components/repoLabel.js'; +import { createRepoLabel } from '@/scene/components/repoLabel/repoLabel'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; import { RENDER_ORDERS } from '@/constants'; import { resetBuildingsConfig } from '../../../_helpers/cityFixtures'; diff --git a/app/tests/scene/components/repoLabel/repoLabelPositioning.test.ts b/app/tests/scene/components/repoLabel/repoLabelPositioning.test.ts index 8bec5b66..d80df1c5 100644 --- a/app/tests/scene/components/repoLabel/repoLabelPositioning.test.ts +++ b/app/tests/scene/components/repoLabel/repoLabelPositioning.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as THREE from 'three'; -import { createRepoLabel } from '@/scene/components/repoLabel/repoLabel.js'; -import { REPO_LABEL } from '@/config/components/repoLabel.js'; +import { createRepoLabel } from '@/scene/components/repoLabel/repoLabel'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; import { resetBuildingsConfig } from '../../../_helpers/cityFixtures'; // Positioning math below assumes BUILDING_DIMENSIONS.MAX_FLOORS=96, diff --git a/app/tests/scene/components/repoLabel/textCanvas.test.ts b/app/tests/scene/components/repoLabel/textCanvas.test.ts index a13c6948..569436c0 100644 --- a/app/tests/scene/components/repoLabel/textCanvas.test.ts +++ b/app/tests/scene/components/repoLabel/textCanvas.test.ts @@ -4,7 +4,7 @@ import { createRepoNameTexture, measureForName, redrawRepoName, -} from '@/scene/components/repoLabel/textCanvas.js'; +} from '@/scene/components/repoLabel/textCanvas'; describe('repoLabel textCanvas', () => { it('createRepoNameTexture returns a CanvasTexture sized for the name', () => { diff --git a/app/tests/scene/components/streets/streetTile.test.ts b/app/tests/scene/components/streets/streetTile.test.ts index dfac7199..056d6e14 100644 --- a/app/tests/scene/components/streets/streetTile.test.ts +++ b/app/tests/scene/components/streets/streetTile.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { SpatialGrid } from '@/scene/layout/spatialGrid.js'; -import { buildCellStreetMesh, type SidewalkRect } from '@/scene/components/streets/streetTile.js'; +import { SpatialGrid } from '@/scene/layout/spatialGrid'; +import { buildCellStreetMesh, type SidewalkRect } from '@/scene/components/streets/streetTile'; describe('streetTile', () => { it('merges rects whose centroid is inside the cell', () => { diff --git a/app/tests/scene/effects/buildingFader.test.ts b/app/tests/scene/effects/buildingFader.test.ts index 2a7449f5..72ce808e 100644 --- a/app/tests/scene/effects/buildingFader.test.ts +++ b/app/tests/scene/effects/buildingFader.test.ts @@ -6,8 +6,8 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as THREE from 'three'; import { atom } from 'nanostores'; -import { createBuildingFader } from '@/scene/effects/buildingFader.js'; -import { BUILDING_FADE } from '@/config/index.js'; +import { createBuildingFader } from '@/scene/effects/buildingFader'; +import { BUILDING_FADE } from '@/state/settings/index'; import { FadeDetail, NodeKind } from '@/types'; import type { Building, DirNode, FileNode, PickTarget, Street } from '@/types'; diff --git a/app/tests/scene/effects/pathLineRenderer.test.ts b/app/tests/scene/effects/pathLineRenderer.test.ts index 04ac5904..6fa76570 100644 --- a/app/tests/scene/effects/pathLineRenderer.test.ts +++ b/app/tests/scene/effects/pathLineRenderer.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, afterEach } from 'vitest'; -import { STREET_TIERS } from '@/config/index.js'; -import { computePathLinewidthPixels } from '@/scene/effects/pathLineRenderer.js'; +import { STREET_TIERS } from '@/state/settings/index'; +import { computePathLinewidthPixels } from '@/scene/effects/pathLineRenderer'; // Capture the original tiers so afterEach can restore them. const _originalTiers = STREET_TIERS.get(); diff --git a/app/tests/scene/effects/treeOutlineRenderer.test.ts b/app/tests/scene/effects/treeOutlineRenderer.test.ts index b44f42e4..e708a678 100644 --- a/app/tests/scene/effects/treeOutlineRenderer.test.ts +++ b/app/tests/scene/effects/treeOutlineRenderer.test.ts @@ -6,11 +6,11 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as THREE from 'three'; import { atom } from 'nanostores'; import { LineSegmentsGeometry } from 'three/addons/lines/LineSegmentsGeometry.js'; -import { createTreeOutlineRenderer } from '@/scene/effects/treeOutlineRenderer.js'; -import { TREE_OUTLINE } from '@/config/components/trees.js'; -import { RAINBOW } from '@/config/effects/effects.js'; +import { createTreeOutlineRenderer } from '@/scene/effects/treeOutlineRenderer'; +import { TREE_OUTLINE } from '@/state/settings/components/trees'; +import { RAINBOW } from '@/state/settings/effects/effects'; import { NodeKind } from '@/types'; -import type { PickTarget } from '@/types/picker.js'; +import type { PickTarget } from '@/types/picker'; function fakeCanvas(): HTMLCanvasElement { const c = document.createElement('canvas'); diff --git a/app/tests/scene/fireflies/authorColor.test.ts b/app/tests/scene/fireflies/authorColor.test.ts index 8499cacd..bf476de6 100644 --- a/app/tests/scene/fireflies/authorColor.test.ts +++ b/app/tests/scene/fireflies/authorColor.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { colorForAuthor } from '@/scene/components/fireflies/authorColor.js'; +import { colorForAuthor } from '@/scene/components/fireflies/authorColor'; describe('colorForAuthor', () => { it('returns the same hex for the same name across calls', () => { diff --git a/app/tests/scene/fireflies/fireflies.test.ts b/app/tests/scene/fireflies/fireflies.test.ts index a04075ec..fb087db9 100644 --- a/app/tests/scene/fireflies/fireflies.test.ts +++ b/app/tests/scene/fireflies/fireflies.test.ts @@ -1,9 +1,9 @@ import { describe, it, expect } from 'vitest'; import * as THREE from 'three'; -import { createFireflies } from '@/scene/components/fireflies/fireflies.js'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { createFireflies } from '@/scene/components/fireflies/fireflies'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; import type { CommitEntry } from '@/types'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; const COMMITS: CommitEntry[] = [ { date: '2026-01-01', files: 1, sha: 'a'.repeat(40), authors: ['Alice'], subject: 'a' }, diff --git a/app/tests/scene/fireflies/firefliesPlacement.test.ts b/app/tests/scene/fireflies/firefliesPlacement.test.ts index 34a4da38..9c1c48c1 100644 --- a/app/tests/scene/fireflies/firefliesPlacement.test.ts +++ b/app/tests/scene/fireflies/firefliesPlacement.test.ts @@ -1,8 +1,8 @@ import { describe, it, expect } from 'vitest'; -import { placeFireflies } from '@/scene/components/fireflies/firefliesPlacement.js'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { placeFireflies } from '@/scene/components/fireflies/firefliesPlacement'; +import { FIREFLIES } from '@/state/settings/components/fireflies'; import type { CommitEntry } from '@/types'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; const COMMITS: CommitEntry[] = [ { date: '2026-01-01', files: 1, sha: 'a'.repeat(40), authors: ['Alice'], subject: 'one' }, diff --git a/app/tests/scene/fireflies/orbitRings.test.ts b/app/tests/scene/fireflies/orbitRings.test.ts index 9507a502..83912f52 100644 --- a/app/tests/scene/fireflies/orbitRings.test.ts +++ b/app/tests/scene/fireflies/orbitRings.test.ts @@ -6,8 +6,8 @@ import { describe, it, expect } from 'vitest'; import * as THREE from 'three'; -import { createOrbitRings } from '@/scene/components/fireflies/orbitRings.js'; -import type { FireflyPlacement } from '@/scene/components/fireflies/firefliesPlacement.js'; +import { createOrbitRings } from '@/scene/components/fireflies/orbitRings'; +import type { FireflyPlacement } from '@/scene/components/fireflies/firefliesPlacement'; const RING_GROUP_NAME = 'firefly-orbit-rings'; diff --git a/app/tests/scene/island/islandGeometry.test.ts b/app/tests/scene/island/islandGeometry.test.ts index c9a4fe4d..b2d67b31 100644 --- a/app/tests/scene/island/islandGeometry.test.ts +++ b/app/tests/scene/island/islandGeometry.test.ts @@ -6,7 +6,7 @@ import { pointInIslandPolygon, type IslandBuildParams, type IslandColors, -} from '@/scene/components/island/islandGeometry.js'; +} from '@/scene/components/island/islandGeometry'; describe('buildTopPolygon', () => { const baseParams: IslandBuildParams = { diff --git a/app/tests/scene/island/islandMesh.test.ts b/app/tests/scene/island/islandMesh.test.ts index 3182afea..82a64d17 100644 --- a/app/tests/scene/island/islandMesh.test.ts +++ b/app/tests/scene/island/islandMesh.test.ts @@ -1,7 +1,7 @@ import * as THREE from 'three'; import { describe, it, expect, beforeEach } from 'vitest'; -import { createIsland } from '@/scene/components/island/islandMesh.js'; -import { ISLAND_GEOMETRY } from '@/config/components/island.js'; +import { createIsland } from '@/scene/components/island/islandMesh'; +import { ISLAND_GEOMETRY } from '@/state/settings/components/island'; import { RENDER_ORDERS } from '@/constants'; describe('createIsland', () => { diff --git a/app/tests/scene/island/islandShader.test.ts b/app/tests/scene/island/islandShader.test.ts index fcb67aab..bb97e6ae 100644 --- a/app/tests/scene/island/islandShader.test.ts +++ b/app/tests/scene/island/islandShader.test.ts @@ -1,6 +1,6 @@ import * as THREE from 'three'; import { describe, it, expect } from 'vitest'; -import { createIslandMaterial } from '@/scene/components/island/islandShader.js'; +import { createIslandMaterial } from '@/scene/components/island/islandShader'; describe('createIslandMaterial', () => { it('returns a ShaderMaterial with all required uniforms', () => { diff --git a/app/tests/scene/layout/cellAssembly.test.ts b/app/tests/scene/layout/cellAssembly.test.ts index 0d33f20e..e364def1 100644 --- a/app/tests/scene/layout/cellAssembly.test.ts +++ b/app/tests/scene/layout/cellAssembly.test.ts @@ -5,8 +5,8 @@ import { describe, it, expect } from 'vitest'; import * as THREE from 'three'; -import { buildCellsFromLayout } from '@/scene/layout/cellAssembly.js'; -import { NodeKind } from '@/types/index.js'; +import { buildCellsFromLayout } from '@/scene/layout/cellAssembly'; +import { NodeKind } from '@/types/index'; import { building } from '../../_helpers/buildingFixture'; const EMPTY_UNIFORMS: Record = {}; diff --git a/app/tests/scene/layout/cellTile.test.ts b/app/tests/scene/layout/cellTile.test.ts index 95008729..7bb29afe 100644 --- a/app/tests/scene/layout/cellTile.test.ts +++ b/app/tests/scene/layout/cellTile.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect } from 'vitest'; import * as THREE from 'three'; -import { SpatialGrid } from '@/scene/layout/spatialGrid.js'; -import { createEmptyCellTile } from '@/scene/layout/cellTile.js'; +import { SpatialGrid } from '@/scene/layout/spatialGrid'; +import { createEmptyCellTile } from '@/scene/layout/cellTile'; describe('CellTile', () => { it('createEmptyCellTile allocates meshes with preallocated capacity', () => { diff --git a/app/tests/scene/layout/layout.test.ts b/app/tests/scene/layout/layout.test.ts index 32a768f0..7ed7f341 100644 --- a/app/tests/scene/layout/layout.test.ts +++ b/app/tests/scene/layout/layout.test.ts @@ -6,12 +6,12 @@ import { sortForRendering, computeLineStats, __test, -} from '@/scene/layout/layout.js'; -import type { Rect } from '@/scene/layout/layout.js'; -import { BUILDING_DIMENSIONS } from '@/config/index.js'; +} from '@/scene/layout/layout'; +import type { Rect } from '@/scene/layout/layout'; +import { BUILDING_DIMENSIONS } from '@/state/settings/index'; import { BuildingOrient, NodeKind, StreetAxis } from '@/types'; -import type { BuildingDimensionsConfig } from '@/config/components/buildings.js'; -import type { StreetTier } from '@/config/components/streets.js'; +import type { BuildingDimensionsConfig } from '@/state/settings/components/buildings'; +import type { StreetTier } from '@/state/settings/components/streets'; import { assertNoOverlap, assertStemOrder, diff --git a/app/tests/scene/layout/layoutClient.test.ts b/app/tests/scene/layout/layoutClient.test.ts index c9a1f4c4..17fb54bb 100644 --- a/app/tests/scene/layout/layoutClient.test.ts +++ b/app/tests/scene/layout/layoutClient.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { createLayoutClient } from '@/scene/layout/layoutClient.js'; +import { createLayoutClient } from '@/scene/layout/layoutClient'; import { NodeKind } from '@/types'; import type { Manifest, FileNode } from '@/types'; diff --git a/app/tests/scene/layout/layoutPacker.test.ts b/app/tests/scene/layout/layoutPacker.test.ts index b02cf9b2..0d50342b 100644 --- a/app/tests/scene/layout/layoutPacker.test.ts +++ b/app/tests/scene/layout/layoutPacker.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { WorldRectKind } from '@/scene/layout/worldOccupancy.js'; +import { WorldRectKind } from '@/scene/layout/worldOccupancy'; import { StreetAxis } from '@/types'; import { applyFlips, diff --git a/app/tests/scene/layout/layoutTrace.test.ts b/app/tests/scene/layout/layoutTrace.test.ts index a8499297..ba40e64d 100644 --- a/app/tests/scene/layout/layoutTrace.test.ts +++ b/app/tests/scene/layout/layoutTrace.test.ts @@ -1,11 +1,11 @@ // layoutTrace.test.ts — exercise the optional `trace` params on -import { WorldRectKind } from '@/scene/layout/worldOccupancy.js'; +import { WorldRectKind } from '@/scene/layout/worldOccupancy'; // findSmallestValidStem, placeChild, and the layoutCityWithTrace entry. import { describe, it, expect } from 'vitest'; -import { findSmallestValidStem, placeChild, layoutCityWithTrace } from '@/scene/layout/layout.js'; -import type { VariantTrace } from '@/scene/layout/layout.js'; -import { WorldOccupancy } from '@/scene/layout/worldOccupancy.js'; +import { findSmallestValidStem, placeChild, layoutCityWithTrace } from '@/scene/layout/layout'; +import type { VariantTrace } from '@/scene/layout/layout'; +import { WorldOccupancy } from '@/scene/layout/worldOccupancy'; import { StreetAxis, NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest } from '@/types'; diff --git a/app/tests/scene/layout/spatialGrid.test.ts b/app/tests/scene/layout/spatialGrid.test.ts index e35e167e..d88468f0 100644 --- a/app/tests/scene/layout/spatialGrid.test.ts +++ b/app/tests/scene/layout/spatialGrid.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { SpatialGrid, CELL_SIZE, MIN_CELL_SIZE } from '@/scene/layout/spatialGrid.js'; +import { SpatialGrid, CELL_SIZE, MIN_CELL_SIZE } from '@/scene/layout/spatialGrid'; describe('SpatialGrid', () => { it('computes grid dimensions from world bounds', () => { diff --git a/app/tests/scene/layout/worldOccupancy.test.ts b/app/tests/scene/layout/worldOccupancy.test.ts index 310c14fe..479270e8 100644 --- a/app/tests/scene/layout/worldOccupancy.test.ts +++ b/app/tests/scene/layout/worldOccupancy.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest'; -import { WorldRectKind } from '@/scene/layout/worldOccupancy.js'; +import { WorldRectKind } from '@/scene/layout/worldOccupancy'; import { WorldOccupancy, type WorldRect } from '@/scene/layout/worldOccupancy'; // Helper to build a WorldRect from {x, y, w, d} for terse tests. diff --git a/app/tests/scene/lighting/fogChunk.test.ts b/app/tests/scene/lighting/fogChunk.test.ts index 06b46242..97a2f5d0 100644 --- a/app/tests/scene/lighting/fogChunk.test.ts +++ b/app/tests/scene/lighting/fogChunk.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { FOG_UNIFORMS_GLSL, FOG_APPLY_GLSL } from '@/scene/components/lighting/fogChunk.js'; +import { FOG_UNIFORMS_GLSL, FOG_APPLY_GLSL } from '@/scene/components/lighting/fogChunk'; describe('fog GLSL chunks', () => { it('uniform declarations include height-fog symbols', () => { diff --git a/app/tests/scene/lighting/sunDir.test.ts b/app/tests/scene/lighting/sunDir.test.ts index e45f7c58..a82a9652 100644 --- a/app/tests/scene/lighting/sunDir.test.ts +++ b/app/tests/scene/lighting/sunDir.test.ts @@ -1,7 +1,7 @@ import * as THREE from 'three'; import { describe, it, expect, beforeEach } from 'vitest'; -import { writeSunDir, sunDirFromLighting } from '@/scene/components/lighting/sunDir.js'; -import { LIGHTING } from '@/config/components/lighting.js'; +import { writeSunDir, sunDirFromLighting } from '@/scene/components/lighting/sunDir'; +import { LIGHTING } from '@/state/settings/components/lighting'; describe('writeSunDir', () => { beforeEach(() => { diff --git a/app/tests/scene/sky/sky.test.ts b/app/tests/scene/sky/sky.test.ts index 836f5e5d..6269e4cd 100644 --- a/app/tests/scene/sky/sky.test.ts +++ b/app/tests/scene/sky/sky.test.ts @@ -6,8 +6,8 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as THREE from 'three'; -import { createSky } from '@/scene/components/sky/sky.js'; -import { SKY, SKY_STARS } from '@/config/components/sky.js'; +import { createSky } from '@/scene/components/sky/sky'; +import { SKY, SKY_STARS } from '@/state/settings/components/sky'; import { RENDER_ORDERS } from '@/constants'; function resetStores() { diff --git a/app/tests/scene/sky/skyConfig.test.ts b/app/tests/scene/sky/skyConfig.test.ts index d3937fbb..72f10500 100644 --- a/app/tests/scene/sky/skyConfig.test.ts +++ b/app/tests/scene/sky/skyConfig.test.ts @@ -5,7 +5,7 @@ // break this test when changed. import { describe, it, expect } from 'vitest'; -import { SKY, SKY_STARS } from '@/config/components/sky.js'; +import { SKY, SKY_STARS } from '@/state/settings/components/sky'; describe('SKY', () => { it('exposes the expected keys with the right types', () => { diff --git a/app/tests/scene/system/cameraRig.test.ts b/app/tests/scene/system/cameraRig.test.ts index af7a2b3c..387c154f 100644 --- a/app/tests/scene/system/cameraRig.test.ts +++ b/app/tests/scene/system/cameraRig.test.ts @@ -3,7 +3,7 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; import * as THREE from 'three'; -import { createCameraRig } from '@/scene/system/cameraRig.js'; +import { createCameraRig } from '@/scene/system/cameraRig'; import { BuildingOrient, NodeKind, StreetAxis } from '@/types'; import type { Building, Street } from '@/types'; diff --git a/app/tests/scene/system/picker-commit.test.ts b/app/tests/scene/system/picker-commit.test.ts index 590d1619..e7be11b6 100644 --- a/app/tests/scene/system/picker-commit.test.ts +++ b/app/tests/scene/system/picker-commit.test.ts @@ -4,7 +4,7 @@ import * as THREE from 'three'; import { describe, it, expect, beforeEach } from 'vitest'; -import { createPicker, PICKER_SELECTION_KEY } from '@/scene/system/picker.js'; +import { createPicker, PICKER_SELECTION_KEY } from '@/scene/system/picker'; import { NodeKind } from '@/types'; import type { CommitEntry, PickerWorld, CommitTarget } from '@/types'; diff --git a/app/tests/scene/system/picker.test.ts b/app/tests/scene/system/picker.test.ts index 9a7d1344..88faadb1 100644 --- a/app/tests/scene/system/picker.test.ts +++ b/app/tests/scene/system/picker.test.ts @@ -4,7 +4,7 @@ import * as THREE from 'three'; import { describe, it, expect, beforeEach } from 'vitest'; -import { createPicker, PICKER_SELECTION_KEY } from '@/scene/system/picker.js'; +import { createPicker, PICKER_SELECTION_KEY } from '@/scene/system/picker'; import { NodeKind } from '@/types'; import type { Building, diff --git a/app/tests/scene/trees/treeEncoding.test.ts b/app/tests/scene/trees/treeEncoding.test.ts index 61eb8c0d..77d367de 100644 --- a/app/tests/scene/trees/treeEncoding.test.ts +++ b/app/tests/scene/trees/treeEncoding.test.ts @@ -15,9 +15,9 @@ import { type AgeRange, type SizeRange, type DailyCountRange, -} from '@/scene/components/trees/treeEncoding.js'; +} from '@/scene/components/trees/treeEncoding'; import type { CommitEntry } from '@/types'; -import { commits as buildCommits } from './_commitFixtures.js'; +import { commits as buildCommits } from './_commitFixtures'; const commits: CommitEntry[] = buildCommits( { date: '2026-01-01', files: 1 }, diff --git a/app/tests/scene/trees/treePlacement.test.ts b/app/tests/scene/trees/treePlacement.test.ts index 40dc23f6..af93d613 100644 --- a/app/tests/scene/trees/treePlacement.test.ts +++ b/app/tests/scene/trees/treePlacement.test.ts @@ -1,8 +1,8 @@ // treePlacement.test.ts — verifies commit-driven tree placement. import { describe, it, expect, beforeEach } from 'vitest'; -import { placeTrees, type TreePlacement } from '@/scene/components/trees/treePlacement.js'; -import { TREES } from '@/config/components/trees.js'; +import { placeTrees, type TreePlacement } from '@/scene/components/trees/treePlacement'; +import { TREES } from '@/state/settings/components/trees'; import type { CityLayout } from '@/types'; import { bbox, @@ -90,7 +90,7 @@ describe('placeTrees (commit-driven)', () => { }); it('rejects candidates inside the FOOTPRINT halo around a layout rect', async () => { - const { FOOTPRINT } = await import('@/config/components/footprint.js'); + const { FOOTPRINT } = await import('@/state/settings/components/footprint.js'); FOOTPRINT.setKey('HALO_WIDTH', 100); const bb = bbox(-500, -500, 500, 500); diff --git a/app/tests/scene/trees/treePlacementClient.test.ts b/app/tests/scene/trees/treePlacementClient.test.ts index cfaf68be..b2be41c3 100644 --- a/app/tests/scene/trees/treePlacementClient.test.ts +++ b/app/tests/scene/trees/treePlacementClient.test.ts @@ -2,7 +2,7 @@ // Worker is unavailable, the supersede protocol, and dispose behavior. import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { createTreePlacementClient } from '@/scene/components/trees/treePlacementClient.js'; +import { createTreePlacementClient } from '@/scene/components/trees/treePlacementClient'; import type { CityLayout } from '@/types'; import { bbox, emptyLayout } from '../../_helpers/cityFixtures'; diff --git a/app/tests/scene/trees/treeRenderer.test.ts b/app/tests/scene/trees/treeRenderer.test.ts index ea84517c..c3155be7 100644 --- a/app/tests/scene/trees/treeRenderer.test.ts +++ b/app/tests/scene/trees/treeRenderer.test.ts @@ -9,14 +9,14 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import * as THREE from 'three'; -import { createTreeRenderer, type Trees } from '@/scene/components/trees/treeRenderer.js'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; -import { TREES } from '@/config/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; -import { LIGHTING } from '@/config/components/lighting.js'; +import { createTreeRenderer, type Trees } from '@/scene/components/trees/treeRenderer'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; +import { TREES } from '@/state/settings/components/trees'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; +import { LIGHTING } from '@/state/settings/components/lighting'; import { RENDER_ORDERS } from '@/constants'; import type { CommitEntry } from '@/types'; -import { commits as buildCommits } from './_commitFixtures.js'; +import { commits as buildCommits } from './_commitFixtures'; function resetStores() { TREES.set({ diff --git a/app/tests/scene/trees/treeRendererCommitLookup.test.ts b/app/tests/scene/trees/treeRendererCommitLookup.test.ts index e661fded..1ddeb19c 100644 --- a/app/tests/scene/trees/treeRendererCommitLookup.test.ts +++ b/app/tests/scene/trees/treeRendererCommitLookup.test.ts @@ -5,10 +5,10 @@ import { describe, it, expect, beforeEach } from 'vitest'; import * as THREE from 'three'; -import { createTreeRenderer } from '@/scene/components/trees/treeRenderer.js'; -import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; -import { TREES } from '@/config/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/config/components/buildings.js'; +import { createTreeRenderer } from '@/scene/components/trees/treeRenderer'; +import type { TreePlacement } from '@/scene/components/trees/treePlacement'; +import { TREES } from '@/state/settings/components/trees'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings'; import type { CommitEntry } from '@/types'; function resetStores() { diff --git a/app/tests/scene/utils/color/colorInterp.test.ts b/app/tests/scene/utils/color/colorInterp.test.ts index 8f575b1c..beec9065 100644 --- a/app/tests/scene/utils/color/colorInterp.test.ts +++ b/app/tests/scene/utils/color/colorInterp.test.ts @@ -1,7 +1,7 @@ // colorInterp.test.ts — verifies the OKLCH interpolation helper. import { describe, it, expect } from 'vitest'; -import { interpolateOklch } from '@/scene/utils/color/colors.js'; +import { interpolateOklch } from '@/scene/utils/color/colors'; function rgb(r: number, g: number, b: number) { return { r, g, b }; diff --git a/app/tests/scene/utils/color/hsl-glsl-parity.test.ts b/app/tests/scene/utils/color/hsl-glsl-parity.test.ts index efb73635..583f1e2a 100644 --- a/app/tests/scene/utils/color/hsl-glsl-parity.test.ts +++ b/app/tests/scene/utils/color/hsl-glsl-parity.test.ts @@ -21,7 +21,7 @@ import { shadeAndShiftHue, shadeByRatio, hslToComponents, -} from '@/scene/utils/color/colors.js'; +} from '@/scene/utils/color/colors'; const __dirname = dirname(fileURLToPath(import.meta.url)); diff --git a/app/tests/scene/utils/color/hsl.test.ts b/app/tests/scene/utils/color/hsl.test.ts index 3bed5e4b..9982cafb 100644 --- a/app/tests/scene/utils/color/hsl.test.ts +++ b/app/tests/scene/utils/color/hsl.test.ts @@ -5,7 +5,7 @@ import { shadeColor, shadeAndShiftHue, shadeByRatio, -} from '@/scene/utils/color/colors.js'; +} from '@/scene/utils/color/colors'; describe('hslToComponents', () => { it('parses a standard hsl() string', () => { diff --git a/app/tests/scene/utils/path.test.ts b/app/tests/scene/utils/path.test.ts index 8b41a18b..ecbfa482 100644 --- a/app/tests/scene/utils/path.test.ts +++ b/app/tests/scene/utils/path.test.ts @@ -4,7 +4,7 @@ import { streetChainForDirPath, computePathPoints, streetEndOpposite, -} from '@/scene/utils/path.js'; +} from '@/scene/utils/path'; import { NodeKind, StreetAxis } from '@/types'; // Local minimal-shape type matches the StreetLike structural contract that diff --git a/app/tests/scene/world/worldBounds.test.ts b/app/tests/scene/world/worldBounds.test.ts index ffa4bcd6..13cb35dd 100644 --- a/app/tests/scene/world/worldBounds.test.ts +++ b/app/tests/scene/world/worldBounds.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { getWorldBounds } from '@/scene/layout/worldBounds.js'; -import { WORLD } from '@/config/world/world.js'; +import { getWorldBounds } from '@/scene/layout/worldBounds'; +import { WORLD } from '@/state/settings/world/world'; import { bbox } from '../../_helpers/cityFixtures'; // All formulas below assume the default GROUND_BUFFER_PERCENT of 30 and diff --git a/app/tests/scene/worldLayoutCache.test.ts b/app/tests/scene/worldLayoutCache.test.ts index 5f17fc7d..e8057741 100644 --- a/app/tests/scene/worldLayoutCache.test.ts +++ b/app/tests/scene/worldLayoutCache.test.ts @@ -17,8 +17,8 @@ // `["invalidateLayoutCache", "applyManifest"]`. Post-fix: passes. import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { attachCommitReactions } from '@/store/configCommitReactions.js'; -import { STREET_LAYOUT } from '@/config/index.js'; +import { attachCommitReactions } from '@/state/reactions'; +import { STREET_LAYOUT } from '@/state/settings/index'; describe('configCommitReactions invalidates layout cache before applyManifest', () => { let calls: string[]; diff --git a/app/tests/config/drafts.test.ts b/app/tests/state/drafts.test.ts similarity index 98% rename from app/tests/config/drafts.test.ts rename to app/tests/state/drafts.test.ts index 5af68a6c..44bb5064 100644 --- a/app/tests/config/drafts.test.ts +++ b/app/tests/state/drafts.test.ts @@ -10,8 +10,8 @@ import { isDirty, subscribe, _resetForTests, -} from '@/store/configDrafts.js'; -import { persistStore } from '@/store/persist.js'; +} from '@/state/drafts'; +import { persistStore } from '@/state/persist'; interface FooConfig { COLOR: string; diff --git a/app/tests/persistPerSource.test.ts b/app/tests/state/persistPerSource.test.ts similarity index 94% rename from app/tests/persistPerSource.test.ts rename to app/tests/state/persistPerSource.test.ts index 24d4d017..ec24ec48 100644 --- a/app/tests/persistPerSource.test.ts +++ b/app/tests/state/persistPerSource.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeEach } from 'vitest'; import { atom } from 'nanostores'; -import { persistAtomPerSource } from '@/store/persist.js'; -import { CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; +import { persistAtomPerSource } from '@/state/persist'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext'; describe('persistAtomPerSource', () => { beforeEach(() => { diff --git a/app/tests/sourceContext.test.ts b/app/tests/state/runtime/sourceContext.test.ts similarity index 92% rename from app/tests/sourceContext.test.ts rename to app/tests/state/runtime/sourceContext.test.ts index c713b86d..bc0579e3 100644 --- a/app/tests/sourceContext.test.ts +++ b/app/tests/state/runtime/sourceContext.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { sourceKey, CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; +import { sourceKey, CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext'; describe('sourceKey', () => { it('is deterministic for the same (src, branch)', () => { diff --git a/app/tests/sourceRecents.test.ts b/app/tests/state/runtime/sourceRecents.test.ts similarity index 96% rename from app/tests/sourceRecents.test.ts rename to app/tests/state/runtime/sourceRecents.test.ts index f75989bc..066e393f 100644 --- a/app/tests/sourceRecents.test.ts +++ b/app/tests/state/runtime/sourceRecents.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { listRecents, pushRecent, removeRecent } from '@/views/source/sourceRecents.js'; +import { listRecents, pushRecent, removeRecent } from '@/state/runtime/sourceRecents'; describe('sourceRecents', () => { beforeEach(() => { diff --git a/app/tests/config/components/repoLabel.test.ts b/app/tests/state/settings/components/repoLabel.test.ts similarity index 90% rename from app/tests/config/components/repoLabel.test.ts rename to app/tests/state/settings/components/repoLabel.test.ts index 17dd5de6..20682e57 100644 --- a/app/tests/config/components/repoLabel.test.ts +++ b/app/tests/state/settings/components/repoLabel.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { REPO_LABEL } from '@/config/components/repoLabel.js'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; describe('REPO_LABEL config store', () => { beforeEach(() => { diff --git a/app/tests/config/footprint.test.ts b/app/tests/state/settings/footprint.test.ts similarity index 88% rename from app/tests/config/footprint.test.ts rename to app/tests/state/settings/footprint.test.ts index 00aae1c5..db717a68 100644 --- a/app/tests/config/footprint.test.ts +++ b/app/tests/state/settings/footprint.test.ts @@ -1,6 +1,6 @@ // app/tests/config/footprint.test.ts import { describe, it, expect } from 'vitest'; -import { FOOTPRINT } from '@/config/components/footprint.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint'; describe('FOOTPRINT', () => { it('exposes the expected keys with the right types', () => { diff --git a/app/tests/config/island.test.ts b/app/tests/state/settings/island.test.ts similarity index 92% rename from app/tests/config/island.test.ts rename to app/tests/state/settings/island.test.ts index 2fdf32ff..433e252b 100644 --- a/app/tests/config/island.test.ts +++ b/app/tests/state/settings/island.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/config/components/island.js'; +import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island'; describe('ISLAND config defaults', () => { it('GEOMETRY exposes the expected keys with the right types', () => { diff --git a/app/tests/views/panes/commitUrl.test.ts b/app/tests/utils/commit.test.ts similarity index 50% rename from app/tests/views/panes/commitUrl.test.ts rename to app/tests/utils/commit.test.ts index bf1c3fc5..1acf4878 100644 --- a/app/tests/views/panes/commitUrl.test.ts +++ b/app/tests/utils/commit.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { commitUrl } from '@/views/panes/commitUrl.js'; +import { commitUrl, sameDayCommitCount } from '@/utils/commit'; +import type { CommitEntry } from '@/types'; describe('commitUrl', () => { it('appends /commit/ for a github URL', () => { @@ -34,3 +35,31 @@ describe('commitUrl', () => { expect(commitUrl('https://github.com/org/repo', '')).toBeNull(); }); }); + +function commit(date: string, sha: string, files = 1): CommitEntry { + return { date, sha, files, authors: ['Test Author'], subject: 'test commit' }; +} + +describe('sameDayCommitCount', () => { + it('counts a single commit on its date', () => { + const target = commit('2026-03-12', 'a'.repeat(40)); + const all = [target, commit('2026-03-11', 'b'.repeat(40))]; + expect(sameDayCommitCount(target, all)).toBe(1); + }); + + it('counts multiple commits sharing the same date', () => { + const target = commit('2026-03-12', 'a'.repeat(40)); + const all = [ + commit('2026-03-12', 'b'.repeat(40)), + target, + commit('2026-03-12', 'c'.repeat(40)), + commit('2026-03-11', 'd'.repeat(40)), + ]; + expect(sameDayCommitCount(target, all)).toBe(3); + }); + + it('returns 0 when target.date is absent from the list', () => { + const target = commit('2026-03-12', 'a'.repeat(40)); + expect(sameDayCommitCount(target, [commit('2026-03-11', 'b'.repeat(40))])).toBe(0); + }); +}); diff --git a/app/tests/utils/dates.test.ts b/app/tests/utils/dates.test.ts index 4bc40fd5..2ca5d048 100644 --- a/app/tests/utils/dates.test.ts +++ b/app/tests/utils/dates.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { formatRelativeAge } from '@/utils/dates.js'; +import { formatRelativeAge } from '@/utils/dates'; const NOW = new Date('2026-05-24T12:00:00Z'); diff --git a/app/tests/views/widgets/displayLabel.test.ts b/app/tests/utils/sources.test.ts similarity index 86% rename from app/tests/views/widgets/displayLabel.test.ts rename to app/tests/utils/sources.test.ts index 8c75936d..cbd8580b 100644 --- a/app/tests/views/widgets/displayLabel.test.ts +++ b/app/tests/utils/sources.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { labelFromManifest, labelFromUrl, toHttpsRepoUrl } from '@/views/widgets/displayLabel.js'; +import { labelFromManifest, labelFromUrl, srcKind, toHttpsRepoUrl } from '@/utils/sources'; import type { Manifest } from '@/types/manifest'; // Test helper: build the minimal Manifest shape labelFromManifest reads, @@ -109,3 +109,17 @@ describe('toHttpsRepoUrl', () => { expect(toHttpsRepoUrl('git@github.com:foo/bar.git')).toBe('https://github.com/foo/bar'); }); }); + +describe('srcKind', () => { + it('classifies https URLs as git', () => { + expect(srcKind('https://github.com/foo/bar.git')).toBe('git'); + }); + it('classifies SSH URLs as git', () => { + expect(srcKind('git@github.com:foo/bar.git')).toBe('git'); + }); + it('classifies local paths as local', () => { + expect(srcKind('/Users/x/repo')).toBe('local'); + expect(srcKind('./relative')).toBe('local'); + expect(srcKind('bare-name')).toBe('local'); + }); +}); diff --git a/app/tests/views/loadingOverlay.test.ts b/app/tests/views/components/loadingOverlay.test.ts similarity index 98% rename from app/tests/views/loadingOverlay.test.ts rename to app/tests/views/components/loadingOverlay.test.ts index b5475c2b..728e206b 100644 --- a/app/tests/views/loadingOverlay.test.ts +++ b/app/tests/views/components/loadingOverlay.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { createLoadingOverlay } from '@/views/source/loadingOverlay.js'; +import { createLoadingOverlay } from '@/views/components/loadingOverlay'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; diff --git a/app/tests/views/sourcePicker.test.ts b/app/tests/views/components/sourcePicker.test.ts similarity index 98% rename from app/tests/views/sourcePicker.test.ts rename to app/tests/views/components/sourcePicker.test.ts index d3ae1529..97601ff2 100644 --- a/app/tests/views/sourcePicker.test.ts +++ b/app/tests/views/components/sourcePicker.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { createSourcePicker } from '@/views/source/sourcePicker.js'; -import { pushRecent } from '@/views/source/sourceRecents.js'; +import { createSourcePicker } from '@/views/components/sourcePicker'; +import { pushRecent } from '@/state/runtime/sourceRecents'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; diff --git a/app/tests/views/panes/commitPane.test.ts b/app/tests/views/panes/commitPane.test.ts index 2d3ed55e..2d93d3c6 100644 --- a/app/tests/views/panes/commitPane.test.ts +++ b/app/tests/views/panes/commitPane.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { buildCommitPane } from '@/views/panes/commitPane.js'; +import { buildCommitPane } from '@/views/panes/commitPane'; import type { CommitEntry } from '@/types'; function resetDom() { diff --git a/app/tests/views/panes/controlsPane.test.ts b/app/tests/views/panes/controlsPane.test.ts index 8081a473..1478d8df 100644 --- a/app/tests/views/panes/controlsPane.test.ts +++ b/app/tests/views/panes/controlsPane.test.ts @@ -1,7 +1,7 @@ import { describe, it, expect, beforeAll } from 'vitest'; -import { buildControlsPane } from '@/views/panes/controlsPane.js'; -import { attachPersistence } from '@/store/persist.js'; -import * as Config from '@/config/index.js'; +import { buildControlsPane } from '@/views/panes/controlsPane'; +import { attachPersistence } from '@/state/persist'; +import * as Config from '@/state/settings/index'; describe('buildControlsPane', () => { it('returns a pane with the Settings header + Keyboard & mouse section first', () => { diff --git a/app/tests/views/panes/filePreviewPane.test.ts b/app/tests/views/panes/filePreviewPane.test.ts index 2a166294..c9e30c36 100644 --- a/app/tests/views/panes/filePreviewPane.test.ts +++ b/app/tests/views/panes/filePreviewPane.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { buildFilePreviewPane } from '@/views/panes/filePreviewPane.js'; -import { showRightSidebar, hideRightSidebar } from '@/views/shell/rightSidebar.js'; +import { buildFilePreviewPane } from '@/views/panes/filePreviewPane'; +import { showRightSidebar, hideRightSidebar } from '@/views/shell/rightSidebar'; import { NodeKind } from '@/types'; import type { FileNode } from '@/types'; diff --git a/app/tests/views/panes/searchPane.test.ts b/app/tests/views/panes/searchPane.test.ts index f7e089c4..e40af5d5 100644 --- a/app/tests/views/panes/searchPane.test.ts +++ b/app/tests/views/panes/searchPane.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { buildSearchPane } from '@/views/panes/searchPane.js'; +import { buildSearchPane } from '@/views/panes/searchPane'; import { NodeKind } from '@/types'; // Minimal manifest fixture — fields that the search pane reads: diff --git a/app/tests/views/panes/streetPane.test.ts b/app/tests/views/panes/streetPane.test.ts index e2a84156..a686e32b 100644 --- a/app/tests/views/panes/streetPane.test.ts +++ b/app/tests/views/panes/streetPane.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { buildStreetPane } from '@/views/panes/streetPane.js'; +import { buildStreetPane } from '@/views/panes/streetPane'; import { NodeKind } from '@/types'; import type { DirNode, FileNode } from '@/types'; diff --git a/app/tests/views/panes/treePane.test.ts b/app/tests/views/panes/treePane.test.ts index 470f0327..33af0c65 100644 --- a/app/tests/views/panes/treePane.test.ts +++ b/app/tests/views/panes/treePane.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { buildTree, buildTreePane } from '@/views/panes/treePane.js'; +import { buildTree, buildTreePane } from '@/views/panes/treePane'; import type { TreeNode } from '@/types'; const TEST_TREE = { diff --git a/app/tests/views/shell/appHeader.test.ts b/app/tests/views/shell/appHeader.test.ts index ae84e42e..3b20cf6b 100644 --- a/app/tests/views/shell/appHeader.test.ts +++ b/app/tests/views/shell/appHeader.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { initAppHeader } from '@/views/shell/appHeader.js'; +import { initAppHeader } from '@/views/shell/appHeader'; function resetDom() { document.body.innerHTML = ` diff --git a/app/tests/views/shell/badge.test.ts b/app/tests/views/shell/badge.test.ts index affa725a..407b9f56 100644 --- a/app/tests/views/shell/badge.test.ts +++ b/app/tests/views/shell/badge.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { makeExtensionBadge } from '@/views/widgets/badge.js'; +import { makeExtensionBadge } from '@/views/components/badge'; const EMPTY_PALETTE: Record = {}; // Match the file-badge HSL formula in CSS (S=60%, L=35%). diff --git a/app/tests/views/shell/fileIcon.test.ts b/app/tests/views/shell/fileIcon.test.ts index 18b1b092..0431ee7c 100644 --- a/app/tests/views/shell/fileIcon.test.ts +++ b/app/tests/views/shell/fileIcon.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { makeFileIcon, makeFolderIcon } from '@/views/widgets/fileIcon.js'; +import { makeFileIcon, makeFolderIcon } from '@/views/components/fileIcon'; // jsdom doesn't actually fetch the icon src — we just validate the // URL the helper picks. The `data-icon-name` data attribute on the diff --git a/app/tests/views/shell/leftSidebar.test.ts b/app/tests/views/shell/leftSidebar.test.ts index bbce206a..b2f0829b 100644 --- a/app/tests/views/shell/leftSidebar.test.ts +++ b/app/tests/views/shell/leftSidebar.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { showLeftSidebar } from '@/views/shell/leftSidebar.js'; +import { showLeftSidebar } from '@/views/shell/leftSidebar'; import { SidebarTab } from '@/types'; // TEST_TREE is structurally compatible with the diff --git a/app/tests/views/shell/tooltip.test.ts b/app/tests/views/shell/tooltip.test.ts index d5c09276..21bd15a2 100644 --- a/app/tests/views/shell/tooltip.test.ts +++ b/app/tests/views/shell/tooltip.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, afterEach } from 'vitest'; -import { showTooltip, hideTooltip, moveTooltip } from '@/views/widgets/tooltip.js'; +import { showTooltip, hideTooltip, moveTooltip } from '@/views/components/tooltip'; describe('tooltip', () => { afterEach(() => { diff --git a/app/tests/views/widgets/commitMetrics.test.ts b/app/tests/views/widgets/commitMetrics.test.ts deleted file mode 100644 index 7028361c..00000000 --- a/app/tests/views/widgets/commitMetrics.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { sameDayCommitCount } from '@/views/widgets/commitMetrics.js'; -import type { CommitEntry } from '@/types'; - -function commit(date: string, sha: string, files = 1): CommitEntry { - return { date, sha, files, authors: ['Test Author'], subject: 'test commit' }; -} - -describe('sameDayCommitCount', () => { - it('counts a single commit on its date', () => { - const target = commit('2026-03-12', 'a'.repeat(40)); - const all = [target, commit('2026-03-11', 'b'.repeat(40))]; - expect(sameDayCommitCount(target, all)).toBe(1); - }); - - it('counts multiple commits sharing the same date', () => { - const target = commit('2026-03-12', 'a'.repeat(40)); - const all = [ - commit('2026-03-12', 'b'.repeat(40)), - target, - commit('2026-03-12', 'c'.repeat(40)), - commit('2026-03-11', 'd'.repeat(40)), - ]; - expect(sameDayCommitCount(target, all)).toBe(3); - }); - - it('returns 0 when target.date is absent from the list', () => { - const target = commit('2026-03-12', 'a'.repeat(40)); - expect(sameDayCommitCount(target, [commit('2026-03-11', 'b'.repeat(40))])).toBe(0); - }); -});