From 194c4e20056785e85368598bc71fb3a6c16371f8 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:23:53 -0400 Subject: [PATCH 01/10] refactor: hoist server calls into top-level app/src/api/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit manifestStream → api/manifest, serverConfig → api/config, url → api/urls, views/panes/commitFetch → api/commit. No behavior change — only file paths. --- app/src/{views/panes/commitFetch.ts => api/commit.ts} | 2 +- app/src/{utils/serverConfig.ts => api/config.ts} | 2 +- app/src/{utils/manifestStream.ts => api/manifest.ts} | 2 +- app/src/{utils/url.ts => api/urls.ts} | 2 +- app/src/main.ts | 6 +++--- app/src/store/liveUpdates.ts | 4 ++-- app/src/views/panes/commitPane.ts | 2 +- .../{utils/serverConfig.test.ts => api/config.test.ts} | 6 +----- app/tests/{manifestStream.test.ts => api/manifest.test.ts} | 2 +- app/tests/{url.test.ts => api/urls.test.ts} | 2 +- 10 files changed, 13 insertions(+), 17 deletions(-) rename app/src/{views/panes/commitFetch.ts => api/commit.ts} (90%) rename app/src/{utils/serverConfig.ts => api/config.ts} (95%) rename app/src/{utils/manifestStream.ts => api/manifest.ts} (96%) rename app/src/{utils/url.ts => api/urls.ts} (95%) rename app/tests/{utils/serverConfig.test.ts => api/config.test.ts} (94%) rename app/tests/{manifestStream.test.ts => api/manifest.test.ts} (97%) rename app/tests/{url.test.ts => api/urls.test.ts} (96%) 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/manifestStream.ts b/app/src/api/manifest.ts similarity index 96% rename from app/src/utils/manifestStream.ts rename to app/src/api/manifest.ts index a260da9e..124ae8da 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. // diff --git a/app/src/utils/url.ts b/app/src/api/urls.ts similarity index 95% rename from app/src/utils/url.ts rename to app/src/api/urls.ts index d578f802..841c44f1 100644 --- a/app/src/utils/url.ts +++ b/app/src/api/urls.ts @@ -1,4 +1,4 @@ -// url.ts — Build API URLs from the page's query params. Pure function so +// api/urls.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. diff --git a/app/src/main.ts b/app/src/main.ts index 2326d756..c72cb943 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -16,7 +16,7 @@ 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 { manifestUrl } from './api/urls.js'; import { _srcKind, _deriveLabel } from './utils/source.js'; import { applyHljsTheme } from './utils/syntaxTheme.js'; import { buildIconAtlas } from './scene/components/buildings/iconAtlas.js'; @@ -24,11 +24,11 @@ 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 { streamManifest } from './api/manifest.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 { getServerConfig } from './api/config.js'; /** * Set document.title to "{label} (pending) — codecity" from a server-emitted diff --git a/app/src/store/liveUpdates.ts b/app/src/store/liveUpdates.ts index ee422399..4bca6677 100644 --- a/app/src/store/liveUpdates.ts +++ b/app/src/store/liveUpdates.ts @@ -15,8 +15,8 @@ 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 { streamManifest } from '@/api/manifest.js'; +import { manifestUrl, signatureUrl } from '@/api/urls.js'; import { _applyDisplayLabel, startRenderLoop } from '@/scene/renderLoop.js'; function _clampPollSeconds(s: number | unknown): number { diff --git a/app/src/views/panes/commitPane.ts b/app/src/views/panes/commitPane.ts index cf2c91e6..f2e55bd6 100644 --- a/app/src/views/panes/commitPane.ts +++ b/app/src/views/panes/commitPane.ts @@ -19,7 +19,7 @@ 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 { fetchCommitDetail } from '@/api/commit.js'; import { colorForAuthor } from '@/scene/components/fireflies/authorColor.js'; interface BuildCommitPaneOpts { 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..50d466e2 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.js'; describe('fetchServerConfig', () => { beforeEach(() => { 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/url.test.ts b/app/tests/api/urls.test.ts similarity index 96% rename from app/tests/url.test.ts rename to app/tests/api/urls.test.ts index 92494c73..9e83a0e9 100644 --- a/app/tests/url.test.ts +++ b/app/tests/api/urls.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { buildApiUrl } from '@/utils/url.js'; +import { buildApiUrl } from '@/api/urls.js'; describe('buildApiUrl', () => { it('forwards src param when present', () => { From 6a5907fc3bf40fc0e9a1f20c3007bbf71f0c983a Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:27:11 -0400 Subject: [PATCH 02/10] refactor: move pure helpers out of views/ into utils/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commitMetrics, formatRelativeAge → dates, commitUrl all become utils/* modules. They don't touch the DOM and shouldn't have been in views/. Pure file moves + import path updates. --- app/src/coordinator.ts | 2 +- app/src/{views/widgets => utils}/commitMetrics.ts | 2 +- app/src/{views/panes => utils}/commitUrl.ts | 2 +- app/src/views/panes/commitPane.ts | 2 +- app/tests/{views/widgets => utils}/commitMetrics.test.ts | 2 +- app/tests/{views/panes => utils}/commitUrl.test.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) rename app/src/{views/widgets => utils}/commitMetrics.ts (81%) rename app/src/{views/panes => utils}/commitUrl.ts (88%) rename app/tests/{views/widgets => utils}/commitMetrics.test.ts (93%) rename app/tests/{views/panes => utils}/commitUrl.test.ts (95%) diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index 152d9a89..fd5fb66b 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -23,7 +23,7 @@ import { showRightSidebar, hideRightSidebar } from './views/shell/rightSidebar.j 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 { sameDayCommitCount } from './utils/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'; diff --git a/app/src/views/widgets/commitMetrics.ts b/app/src/utils/commitMetrics.ts similarity index 81% rename from app/src/views/widgets/commitMetrics.ts rename to app/src/utils/commitMetrics.ts index 65cc623d..10c5b8c8 100644 --- a/app/src/views/widgets/commitMetrics.ts +++ b/app/src/utils/commitMetrics.ts @@ -1,4 +1,4 @@ -// views/widgets/commitMetrics.ts — pure helpers for per-commit UI metrics +// utils/commitMetrics.ts — pure helpers for per-commit UI metrics // used by the commit pane and (eventually) other views. import type { CommitEntry } from '@/types'; diff --git a/app/src/views/panes/commitUrl.ts b/app/src/utils/commitUrl.ts similarity index 88% rename from app/src/views/panes/commitUrl.ts rename to app/src/utils/commitUrl.ts index 28384cc2..6e894509 100644 --- a/app/src/views/panes/commitUrl.ts +++ b/app/src/utils/commitUrl.ts @@ -1,4 +1,4 @@ -// views/panes/commitUrl.ts — build a browseable commit URL from the +// utils/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 diff --git a/app/src/views/panes/commitPane.ts b/app/src/views/panes/commitPane.ts index f2e55bd6..974895cf 100644 --- a/app/src/views/panes/commitPane.ts +++ b/app/src/views/panes/commitPane.ts @@ -17,7 +17,7 @@ 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 { commitUrl } from '@/utils/commitUrl.js'; import { formatRelativeAge, formatFullDate } from '@/utils/dates.js'; import { fetchCommitDetail } from '@/api/commit.js'; import { colorForAuthor } from '@/scene/components/fireflies/authorColor.js'; diff --git a/app/tests/views/widgets/commitMetrics.test.ts b/app/tests/utils/commitMetrics.test.ts similarity index 93% rename from app/tests/views/widgets/commitMetrics.test.ts rename to app/tests/utils/commitMetrics.test.ts index 7028361c..cc7137b6 100644 --- a/app/tests/views/widgets/commitMetrics.test.ts +++ b/app/tests/utils/commitMetrics.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { sameDayCommitCount } from '@/views/widgets/commitMetrics.js'; +import { sameDayCommitCount } from '@/utils/commitMetrics.js'; import type { CommitEntry } from '@/types'; function commit(date: string, sha: string, files = 1): CommitEntry { diff --git a/app/tests/views/panes/commitUrl.test.ts b/app/tests/utils/commitUrl.test.ts similarity index 95% rename from app/tests/views/panes/commitUrl.test.ts rename to app/tests/utils/commitUrl.test.ts index bf1c3fc5..9ffa2f4b 100644 --- a/app/tests/views/panes/commitUrl.test.ts +++ b/app/tests/utils/commitUrl.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { commitUrl } from '@/views/panes/commitUrl.js'; +import { commitUrl } from '@/utils/commitUrl.js'; describe('commitUrl', () => { it('appends /commit/ for a github URL', () => { From e31865ab29d53eed82c4d534d25873b0421907ec Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:32:27 -0400 Subject: [PATCH 03/10] =?UTF-8?q?refactor:=20merge=20utils/source.ts=20+?= =?UTF-8?q?=20views/widgets/displayLabel.ts=20=E2=86=92=20utils/sources.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both modules did URL/path → label work; the duplication was the exact "broken out by type" smell. Consolidate into one module: srcKind() replaces _srcKind; labelFromUrl(src) ?? src replaces _deriveLabel; toHttpsRepoUrl + labelFromManifest preserved as-is. displayLabel.ts moves alongside the merge; source.ts is deleted. --- app/src/coordinator.ts | 2 +- app/src/main.ts | 17 +++++------ app/src/scene/renderLoop.ts | 2 +- app/src/utils/source.ts | 19 ------------ .../displayLabel.ts => utils/sources.ts} | 29 ++++++++++--------- app/src/views/shell/appHeader.ts | 2 +- .../sources.test.ts} | 16 +++++++++- 7 files changed, 42 insertions(+), 45 deletions(-) delete mode 100644 app/src/utils/source.ts rename app/src/{views/widgets/displayLabel.ts => utils/sources.ts} (75%) rename app/tests/{views/widgets/displayLabel.test.ts => utils/sources.test.ts} (86%) diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index fd5fb66b..f6f17ef9 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -24,7 +24,7 @@ import { buildFilePreviewPane, humanLanguageFor } from './views/panes/filePrevie import { buildCommitPane } from './views/panes/commitPane.js'; import { buildStreetPane } from './views/panes/streetPane.js'; import { sameDayCommitCount } from './utils/commitMetrics.js'; -import { labelFromManifest } from './views/widgets/displayLabel.js'; +import { labelFromManifest } from './utils/sources.js'; import { LIVE_UPDATES } from './config/index.js'; import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './store/liveStatus.js'; import { DateSource, NodeKind } from './types'; diff --git a/app/src/main.ts b/app/src/main.ts index c72cb943..1f5abd17 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -17,7 +17,7 @@ import type { Manifest } from './types'; import { PICKER_SELECTION_KEY } from './scene/system/picker.js'; import { manifestUrl } from './api/urls.js'; -import { _srcKind, _deriveLabel } from './utils/source.js'; +import { srcKind, labelFromUrl } from './utils/sources.js'; import { applyHljsTheme } from './utils/syntaxTheme.js'; import { buildIconAtlas } from './scene/components/buildings/iconAtlas.js'; import { setIconAtlas } from './scene/components/buildings/buildings.js'; @@ -27,7 +27,6 @@ import { createLoadingOverlay } from './views/source/loadingOverlay.js'; import { streamManifest } from './api/manifest.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 './api/config.js'; /** @@ -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/renderLoop.ts b/app/src/scene/renderLoop.ts index 3ad4c6d2..7d3374f1 100644 --- a/app/src/scene/renderLoop.ts +++ b/app/src/scene/renderLoop.ts @@ -36,7 +36,7 @@ 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 { labelFromManifest } from '@/utils/sources.js'; // 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/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/shell/appHeader.ts b/app/src/views/shell/appHeader.ts index 2e5c3a78..a96db79e 100644 --- a/app/src/views/shell/appHeader.ts +++ b/app/src/views/shell/appHeader.ts @@ -9,7 +9,7 @@ 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 { toHttpsRepoUrl } from '@/utils/sources.js'; import { fitSegments } from '../widgets/pathTruncate.js'; // How long the "Copied!" badge lingers after the copy button is clicked. 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..95439b3c 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.js'; 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'); + }); +}); From 1c855b8772796815875761088c905c8fbc7cb531 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:43:48 -0400 Subject: [PATCH 04/10] =?UTF-8?q?refactor:=20move=20localFlag=20+=20source?= =?UTF-8?q?Recents=20out=20of=20views/source/=20=E2=86=92=20utils/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both are pure localStorage helpers; no DOM. views/source/ shouldn't hold non-DOM code. localFlag was already used by views/shell/ leftSidebar.ts — the source/ location was misleading from the start. --- app/src/main.ts | 2 +- app/src/{views/source => utils}/localFlag.ts | 2 +- app/src/{views/source => utils}/sourceRecents.ts | 2 +- app/src/views/shell/leftSidebar.ts | 2 +- app/src/views/source/sourcePicker.ts | 2 +- app/tests/{ => utils}/sourceRecents.test.ts | 2 +- app/tests/views/sourcePicker.test.ts | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) rename app/src/{views/source => utils}/localFlag.ts (95%) rename app/src/{views/source => utils}/sourceRecents.ts (96%) rename app/tests/{ => utils}/sourceRecents.test.ts (96%) diff --git a/app/src/main.ts b/app/src/main.ts index 1f5abd17..b7ecd10b 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -25,7 +25,7 @@ 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 './api/manifest.js'; -import { pushRecent } from './views/source/sourceRecents.js'; +import { pushRecent } from './utils/sourceRecents.js'; import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js'; import { getServerConfig } from './api/config.js'; diff --git a/app/src/views/source/localFlag.ts b/app/src/utils/localFlag.ts similarity index 95% rename from app/src/views/source/localFlag.ts rename to app/src/utils/localFlag.ts index 6ccdd337..c94c0637 100644 --- a/app/src/views/source/localFlag.ts +++ b/app/src/utils/localFlag.ts @@ -1,4 +1,4 @@ -// views/source/localFlag.ts — boolean flags persisted in localStorage. +// utils/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/views/source/sourceRecents.ts b/app/src/utils/sourceRecents.ts similarity index 96% rename from app/src/views/source/sourceRecents.ts rename to app/src/utils/sourceRecents.ts index 22cbee09..e7dd24f4 100644 --- a/app/src/views/source/sourceRecents.ts +++ b/app/src/utils/sourceRecents.ts @@ -1,4 +1,4 @@ -// sourceRecents.ts — Tiny localStorage-backed list of recently-opened +// utils/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'; diff --git a/app/src/views/shell/leftSidebar.ts b/app/src/views/shell/leftSidebar.ts index 67a8c6a3..826e6d3b 100644 --- a/app/src/views/shell/leftSidebar.ts +++ b/app/src/views/shell/leftSidebar.ts @@ -12,7 +12,7 @@ import { buildSearchPane } from '@/views/panes/searchPane.js'; 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 '@/utils/localFlag.js'; const SIDEBAR_MIN_WIDTH = 280; const SIDEBAR_MAX_WIDTH = 600; diff --git a/app/src/views/source/sourcePicker.ts b/app/src/views/source/sourcePicker.ts index 4acd709e..0f67f3dc 100644 --- a/app/src/views/source/sourcePicker.ts +++ b/app/src/views/source/sourcePicker.ts @@ -2,7 +2,7 @@ // 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 '@/utils/sourceRecents.js'; import { LUCIDE_ICON_BASE_URL } from '@/constants'; // ── Hosting-site SVG icons ─────────────────────────────────────────────────── diff --git a/app/tests/sourceRecents.test.ts b/app/tests/utils/sourceRecents.test.ts similarity index 96% rename from app/tests/sourceRecents.test.ts rename to app/tests/utils/sourceRecents.test.ts index f75989bc..23d56f00 100644 --- a/app/tests/sourceRecents.test.ts +++ b/app/tests/utils/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 '@/utils/sourceRecents.js'; describe('sourceRecents', () => { beforeEach(() => { diff --git a/app/tests/views/sourcePicker.test.ts b/app/tests/views/sourcePicker.test.ts index d3ae1529..c8d58850 100644 --- a/app/tests/views/sourcePicker.test.ts +++ b/app/tests/views/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 { pushRecent } from '@/utils/sourceRecents.js'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; From 6074da0833eaca27d8a6d5b00646415dd0dd9a5d Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:48:01 -0400 Subject: [PATCH 05/10] =?UTF-8?q?refactor:=20dissolve=20views/source/=20?= =?UTF-8?q?=E2=80=94=20sourcePicker=20+=20loadingOverlay=20=E2=86=92=20vie?= =?UTF-8?q?ws/widgets/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both are reusable DOM components; "source" was a feature folder breaking the codebase's type-based grouping rule. Landing them in views/widgets/ first; Task 6 renames widgets/ → components/. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/src/main.ts | 4 ++-- app/src/views/{source => widgets}/loadingOverlay.ts | 2 +- app/src/views/{source => widgets}/sourcePicker.ts | 2 +- app/tests/views/{ => widgets}/loadingOverlay.test.ts | 2 +- app/tests/views/{ => widgets}/sourcePicker.test.ts | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) rename app/src/views/{source => widgets}/loadingOverlay.ts (98%) rename app/src/views/{source => widgets}/sourcePicker.ts (99%) rename app/tests/views/{ => widgets}/loadingOverlay.test.ts (98%) rename app/tests/views/{ => widgets}/sourcePicker.test.ts (99%) diff --git a/app/src/main.ts b/app/src/main.ts index b7ecd10b..b89effab 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -22,8 +22,8 @@ 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 { createSourcePicker, type SourcePayload } from './views/widgets/sourcePicker.js'; +import { createLoadingOverlay } from './views/widgets/loadingOverlay.js'; import { streamManifest } from './api/manifest.js'; import { pushRecent } from './utils/sourceRecents.js'; import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js'; diff --git a/app/src/views/source/loadingOverlay.ts b/app/src/views/widgets/loadingOverlay.ts similarity index 98% rename from app/src/views/source/loadingOverlay.ts rename to app/src/views/widgets/loadingOverlay.ts index 4a953963..d2a4b978 100644 --- a/app/src/views/source/loadingOverlay.ts +++ b/app/src/views/widgets/loadingOverlay.ts @@ -1,4 +1,4 @@ -// loadingOverlay.ts — Centered spinner + stepped progress indicator shown +// views/widgets/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/source/sourcePicker.ts b/app/src/views/widgets/sourcePicker.ts similarity index 99% rename from app/src/views/source/sourcePicker.ts rename to app/src/views/widgets/sourcePicker.ts index 0f67f3dc..ef0a3a41 100644 --- a/app/src/views/source/sourcePicker.ts +++ b/app/src/views/widgets/sourcePicker.ts @@ -1,4 +1,4 @@ -// sourcePicker.ts — Modal for picking the active source (local path or +// views/widgets/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. diff --git a/app/tests/views/loadingOverlay.test.ts b/app/tests/views/widgets/loadingOverlay.test.ts similarity index 98% rename from app/tests/views/loadingOverlay.test.ts rename to app/tests/views/widgets/loadingOverlay.test.ts index b5475c2b..83b672f0 100644 --- a/app/tests/views/loadingOverlay.test.ts +++ b/app/tests/views/widgets/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/widgets/loadingOverlay.js'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; diff --git a/app/tests/views/sourcePicker.test.ts b/app/tests/views/widgets/sourcePicker.test.ts similarity index 99% rename from app/tests/views/sourcePicker.test.ts rename to app/tests/views/widgets/sourcePicker.test.ts index c8d58850..e9399430 100644 --- a/app/tests/views/sourcePicker.test.ts +++ b/app/tests/views/widgets/sourcePicker.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { createSourcePicker } from '@/views/source/sourcePicker.js'; +import { createSourcePicker } from '@/views/widgets/sourcePicker.js'; import { pushRecent } from '@/utils/sourceRecents.js'; function mountRoot(): HTMLElement { From 7fb849ea59f60d16c35a2911797582abee536b97 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:49:32 -0400 Subject: [PATCH 06/10] =?UTF-8?q?refactor:=20rename=20views/widgets/=20?= =?UTF-8?q?=E2=86=92=20views/components/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit After Task 2-5 cleanup the directory only holds DOM-producing reusable elements. "Components" describes them more accurately than "widgets". Pure rename + import path rewrite. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/src/config/components/buildings.ts | 2 +- app/src/config/system/tooltip.ts | 2 +- app/src/main.ts | 4 ++-- app/src/scene/components/buildings/buildingsCell.ts | 2 +- app/src/scene/components/buildings/iconAtlas.ts | 2 +- app/src/scene/renderLoop.ts | 2 +- app/src/scene/system/inputHandlers.ts | 2 +- app/src/styles.css | 2 +- app/src/views/{widgets => components}/badge.ts | 2 +- app/src/views/{widgets => components}/fileIcon.ts | 2 +- app/src/views/{widgets => components}/icon.ts | 2 +- app/src/views/{widgets => components}/loadingOverlay.ts | 2 +- app/src/views/{widgets => components}/pathTruncate.ts | 2 +- app/src/views/{widgets => components}/sourcePicker.ts | 2 +- app/src/views/{widgets => components}/tooltip.ts | 2 +- app/src/views/panes/commitPane.ts | 2 +- app/src/views/panes/controlsPane.ts | 2 +- app/src/views/panes/filePreviewPane.ts | 4 ++-- app/src/views/panes/infoPane.ts | 2 +- app/src/views/panes/searchPane.ts | 2 +- app/src/views/panes/streetPane.ts | 4 ++-- app/src/views/panes/treePane.ts | 4 ++-- app/src/views/shell/appHeader.ts | 6 +++--- app/src/views/shell/paneHeader.ts | 2 +- .../views/{widgets => components}/loadingOverlay.test.ts | 2 +- .../views/{widgets => components}/sourcePicker.test.ts | 2 +- app/tests/views/shell/badge.test.ts | 2 +- app/tests/views/shell/fileIcon.test.ts | 2 +- app/tests/views/shell/tooltip.test.ts | 2 +- 29 files changed, 35 insertions(+), 35 deletions(-) rename app/src/views/{widgets => components}/badge.ts (98%) rename app/src/views/{widgets => components}/fileIcon.ts (99%) rename app/src/views/{widgets => components}/icon.ts (96%) rename app/src/views/{widgets => components}/loadingOverlay.ts (98%) rename app/src/views/{widgets => components}/pathTruncate.ts (98%) rename app/src/views/{widgets => components}/sourcePicker.ts (99%) rename app/src/views/{widgets => components}/tooltip.ts (95%) rename app/tests/views/{widgets => components}/loadingOverlay.test.ts (98%) rename app/tests/views/{widgets => components}/sourcePicker.test.ts (99%) diff --git a/app/src/config/components/buildings.ts b/app/src/config/components/buildings.ts index 86f9f91c..a5343d25 100644 --- a/app/src/config/components/buildings.ts +++ b/app/src/config/components/buildings.ts @@ -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/system/tooltip.ts b/app/src/config/system/tooltip.ts index 77fb59fb..eb32d90d 100644 --- a/app/src/config/system/tooltip.ts +++ b/app/src/config/system/tooltip.ts @@ -1,5 +1,5 @@ // config/system/tooltip.ts — Tooltip placement. Consumed by -// views/widgets/tooltip.ts. +// views/components/tooltip.ts. import { map } from 'nanostores'; diff --git a/app/src/main.ts b/app/src/main.ts index b89effab..449c1ced 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -22,8 +22,8 @@ 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/widgets/sourcePicker.js'; -import { createLoadingOverlay } from './views/widgets/loadingOverlay.js'; +import { createSourcePicker, type SourcePayload } from './views/components/sourcePicker.js'; +import { createLoadingOverlay } from './views/components/loadingOverlay.js'; import { streamManifest } from './api/manifest.js'; import { pushRecent } from './utils/sourceRecents.js'; import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js'; diff --git a/app/src/scene/components/buildings/buildingsCell.ts b/app/src/scene/components/buildings/buildingsCell.ts index 2dde9c4d..3870f38b 100644 --- a/app/src/scene/components/buildings/buildingsCell.ts +++ b/app/src/scene/components/buildings/buildingsCell.ts @@ -17,7 +17,7 @@ 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 { getFileIconName } from '@/views/components/fileIcon.js'; import { seedFromPath, attachLeanAwareRaycast } from './buildingTilt.js'; // --------------------------------------------------------------------------- diff --git a/app/src/scene/components/buildings/iconAtlas.ts b/app/src/scene/components/buildings/iconAtlas.ts index 783b8efe..e5f807c0 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.js'; // 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/renderLoop.ts b/app/src/scene/renderLoop.ts index 7d3374f1..9e1173cb 100644 --- a/app/src/scene/renderLoop.ts +++ b/app/src/scene/renderLoop.ts @@ -33,7 +33,7 @@ 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 { showTooltip, hideTooltip } from '../views/components/tooltip.js'; import { createPostFx } from './system/postFx.js'; import { registerRenderer as registerAdPanelRenderer } from './components/adPanels/adPanelTextureArray.js'; import { labelFromManifest } from '@/utils/sources.js'; diff --git a/app/src/scene/system/inputHandlers.ts b/app/src/scene/system/inputHandlers.ts index 03a85b63..3a1b9335 100644 --- a/app/src/scene/system/inputHandlers.ts +++ b/app/src/scene/system/inputHandlers.ts @@ -5,7 +5,7 @@ // 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(); 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/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..74d27d7c 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 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/widgets/loadingOverlay.ts b/app/src/views/components/loadingOverlay.ts similarity index 98% rename from app/src/views/widgets/loadingOverlay.ts rename to app/src/views/components/loadingOverlay.ts index d2a4b978..2c4bed3d 100644 --- a/app/src/views/widgets/loadingOverlay.ts +++ b/app/src/views/components/loadingOverlay.ts @@ -1,4 +1,4 @@ -// views/widgets/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/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/widgets/sourcePicker.ts b/app/src/views/components/sourcePicker.ts similarity index 99% rename from app/src/views/widgets/sourcePicker.ts rename to app/src/views/components/sourcePicker.ts index ef0a3a41..a0ebc5e8 100644 --- a/app/src/views/widgets/sourcePicker.ts +++ b/app/src/views/components/sourcePicker.ts @@ -1,4 +1,4 @@ -// views/widgets/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. diff --git a/app/src/views/widgets/tooltip.ts b/app/src/views/components/tooltip.ts similarity index 95% rename from app/src/views/widgets/tooltip.ts rename to app/src/views/components/tooltip.ts index 681a3f5d..992be173 100644 --- a/app/src/views/widgets/tooltip.ts +++ b/app/src/views/components/tooltip.ts @@ -1,4 +1,4 @@ -// 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 diff --git a/app/src/views/panes/commitPane.ts b/app/src/views/panes/commitPane.ts index 974895cf..5fea0b18 100644 --- a/app/src/views/panes/commitPane.ts +++ b/app/src/views/panes/commitPane.ts @@ -15,7 +15,7 @@ // sidebar without churn. import type { CommitEntry } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; import { commitUrl } from '@/utils/commitUrl.js'; import { formatRelativeAge, formatFullDate } from '@/utils/dates.js'; diff --git a/app/src/views/panes/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 31a20d0a..8c48d651 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -71,7 +71,7 @@ import { } from '@/store/configDrafts.js'; import { KEY_BINDINGS } from '@/constants'; import { FadeDetail } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; // Structural store shape used by all the widget builders. Covers nanostores diff --git a/app/src/views/panes/filePreviewPane.ts b/app/src/views/panes/filePreviewPane.ts index 7795f336..5aa6e825 100644 --- a/app/src/views/panes/filePreviewPane.ts +++ b/app/src/views/panes/filePreviewPane.ts @@ -13,9 +13,9 @@ import hljs from 'highlight.js/lib/common'; import { ASPHALT, BUILDING_PALETTE } from '@/config'; import { PreviewKind } from '@/types'; import type { FileNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/widgets/badge.js'; +import { makeExtensionBadge } from '@/views/components/badge.js'; // 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..f4eae776 100644 --- a/app/src/views/panes/infoPane.ts +++ b/app/src/views/panes/infoPane.ts @@ -7,7 +7,7 @@ import { marked } from 'marked'; import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; // Match README, README.md, readme.markdown, README.txt — any file whose diff --git a/app/src/views/panes/searchPane.ts b/app/src/views/panes/searchPane.ts index 6ce7f6e2..36c73a42 100644 --- a/app/src/views/panes/searchPane.ts +++ b/app/src/views/panes/searchPane.ts @@ -17,7 +17,7 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; const MAX_RESULTS = 50; diff --git a/app/src/views/panes/streetPane.ts b/app/src/views/panes/streetPane.ts index 7c6a6578..adb315dc 100644 --- a/app/src/views/panes/streetPane.ts +++ b/app/src/views/panes/streetPane.ts @@ -9,9 +9,9 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/widgets/icon.js'; +import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/widgets/badge.js'; +import { makeExtensionBadge } from '@/views/components/badge.js'; import { ASPHALT, BUILDING_PALETTE } from '@/config'; interface BuildStreetPaneOpts { diff --git a/app/src/views/panes/treePane.ts b/app/src/views/panes/treePane.ts index 2cff50b3..5282f279 100644 --- a/app/src/views/panes/treePane.ts +++ b/app/src/views/panes/treePane.ts @@ -5,8 +5,8 @@ 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 { makeGemIcon, makeLucideIcon } from '@/views/components/icon.js'; +import { makeFileIcon, makeFolderIcon } from '@/views/components/fileIcon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; interface TreeCtx { diff --git a/app/src/views/shell/appHeader.ts b/app/src/views/shell/appHeader.ts index a96db79e..ee603c16 100644 --- a/app/src/views/shell/appHeader.ts +++ b/app/src/views/shell/appHeader.ts @@ -7,10 +7,10 @@ // 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 { makeGemIcon, makeLucideIcon } from '../components/icon.js'; +import { makeExtensionBadge } from '../components/badge.js'; import { toHttpsRepoUrl } from '@/utils/sources.js'; -import { fitSegments } from '../widgets/pathTruncate.js'; +import { fitSegments } from '../components/pathTruncate.js'; // 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/paneHeader.ts b/app/src/views/shell/paneHeader.ts index a05c3844..7b105b1d 100644 --- a/app/src/views/shell/paneHeader.ts +++ b/app/src/views/shell/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.js'; interface BuildPaneHeaderOpts { /** Initial title text. Update later via the returned api.setTitle. */ diff --git a/app/tests/views/widgets/loadingOverlay.test.ts b/app/tests/views/components/loadingOverlay.test.ts similarity index 98% rename from app/tests/views/widgets/loadingOverlay.test.ts rename to app/tests/views/components/loadingOverlay.test.ts index 83b672f0..dde2a32b 100644 --- a/app/tests/views/widgets/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/widgets/loadingOverlay.js'; +import { createLoadingOverlay } from '@/views/components/loadingOverlay.js'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; diff --git a/app/tests/views/widgets/sourcePicker.test.ts b/app/tests/views/components/sourcePicker.test.ts similarity index 99% rename from app/tests/views/widgets/sourcePicker.test.ts rename to app/tests/views/components/sourcePicker.test.ts index e9399430..30de024b 100644 --- a/app/tests/views/widgets/sourcePicker.test.ts +++ b/app/tests/views/components/sourcePicker.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { createSourcePicker } from '@/views/widgets/sourcePicker.js'; +import { createSourcePicker } from '@/views/components/sourcePicker.js'; import { pushRecent } from '@/utils/sourceRecents.js'; function mountRoot(): HTMLElement { diff --git a/app/tests/views/shell/badge.test.ts b/app/tests/views/shell/badge.test.ts index affa725a..f513392a 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.js'; 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..a35b4ff9 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.js'; // 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/tooltip.test.ts b/app/tests/views/shell/tooltip.test.ts index d5c09276..120a08c1 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.js'; describe('tooltip', () => { afterEach(() => { From 47c0bb0eda8ab1f2c504431225cddefc3028d79b Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:54:24 -0400 Subject: [PATCH 07/10] =?UTF-8?q?refactor:=20rename=20config/=20=E2=86=92?= =?UTF-8?q?=20state/settings/?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit User-tunable nanostores move under a state/ parent shared with the runtime atoms. config/ was a misleading name — both config/ and store/ held nanostores; this rename surfaces their actual purpose. Subfolder structure (components/, effects/, prefs/, system/, world/) preserved. Pure rename + import path rewrite. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/src/coordinator.ts | 2 +- app/src/main.ts | 4 +-- .../components/adPanels/adPanelsInstanced.ts | 2 +- .../components/buildings/buildingColor.ts | 2 +- .../components/buildings/buildingTilt.ts | 2 +- .../scene/components/buildings/buildings.ts | 2 +- .../components/buildings/buildingsCell.ts | 2 +- .../scene/components/fireflies/fireflies.ts | 2 +- .../fireflies/firefliesPlacement.ts | 4 +-- .../components/fireflies/firefliesRenderer.ts | 2 +- .../scene/components/fireflies/orbitRings.ts | 4 +-- .../scene/components/footprint/footprint.ts | 2 +- app/src/scene/components/gem/gem.ts | 7 ++++- app/src/scene/components/island/islandMesh.ts | 2 +- .../scene/components/island/islandShader.ts | 2 +- app/src/scene/components/labels/labelAtlas.ts | 2 +- app/src/scene/components/labels/labelsCell.ts | 2 +- app/src/scene/components/lighting/sunDir.ts | 2 +- .../scene/components/repoLabel/repoLabel.ts | 4 +-- app/src/scene/components/sky/sky.ts | 4 +-- .../scene/components/streets/streetLabels.ts | 2 +- app/src/scene/components/streets/streets.ts | 2 +- .../scene/components/trees/treePlacement.ts | 10 +++---- .../components/trees/treePlacementClient.ts | 10 +++---- .../components/trees/treePlacementWorker.ts | 10 +++---- .../scene/components/trees/treeRenderer.ts | 2 +- app/src/scene/effects/buildingFader.ts | 2 +- app/src/scene/effects/outlineRenderer.ts | 2 +- app/src/scene/effects/pathLineRenderer.ts | 2 +- app/src/scene/effects/treeOutlineRenderer.ts | 4 +-- app/src/scene/layout/layout.ts | 9 +++++-- app/src/scene/layout/layoutClient.ts | 7 ++++- app/src/scene/layout/layoutWorker.ts | 7 ++++- app/src/scene/layout/worldBounds.ts | 2 +- app/src/scene/renderLoop.ts | 4 +-- app/src/scene/system/animator.ts | 2 +- app/src/scene/system/cameraRig.ts | 2 +- app/src/scene/system/inputHandlers.ts | 2 +- app/src/scene/system/postFx.ts | 2 +- app/src/scene/world.ts | 6 ++--- .../settings}/components/adPanels.ts | 2 +- .../settings}/components/buildings.ts | 2 +- .../settings}/components/facade.ts | 2 +- .../settings}/components/fireflies.ts | 2 +- .../settings}/components/footprint.ts | 2 +- .../settings}/components/gem.ts | 2 +- .../settings}/components/island.ts | 2 +- .../settings}/components/lighting.ts | 2 +- .../settings}/components/repoLabel.ts | 2 +- .../settings}/components/sky.ts | 2 +- .../settings}/components/streets.ts | 2 +- .../settings}/components/trees.ts | 2 +- .../settings}/effects/effects.ts | 2 +- app/src/{config => state/settings}/index.ts | 2 +- .../settings}/prefs/liveUpdates.ts | 2 +- .../settings}/prefs/syntaxTheme.ts | 2 +- .../settings}/system/animator.ts | 2 +- .../settings}/system/cameraRig.ts | 2 +- .../settings}/system/inputHandlers.ts | 2 +- .../settings}/system/tooltip.ts | 2 +- .../{config => state/settings}/world/world.ts | 2 +- app/src/store/configCommitReactions.ts | 6 ++--- app/src/store/liveUpdates.ts | 2 +- app/src/views/components/tooltip.ts | 2 +- app/src/views/panes/controlsPane.ts | 26 +++++++++++-------- app/src/views/panes/filePreviewPane.ts | 2 +- app/src/views/panes/streetPane.ts | 2 +- app/src/views/shell/appHeader.ts | 2 +- app/tests/_helpers/cityFixtures.ts | 4 +-- app/tests/_helpers/typography.ts | 2 +- .../scene/cityFootprint/footprint.test.ts | 2 +- .../adPanels/instanced-ad-panels.test.ts | 2 +- .../buildings/buildingColor.test.ts | 4 +-- .../labels/instanced-labels-cell.test.ts | 2 +- .../components/repoLabel/repoLabel.test.ts | 2 +- .../repoLabel/repoLabelPositioning.test.ts | 2 +- app/tests/scene/effects/buildingFader.test.ts | 2 +- .../scene/effects/pathLineRenderer.test.ts | 2 +- .../scene/effects/treeOutlineRenderer.test.ts | 4 +-- app/tests/scene/fireflies/fireflies.test.ts | 2 +- .../fireflies/firefliesPlacement.test.ts | 2 +- app/tests/scene/island/islandMesh.test.ts | 2 +- app/tests/scene/layout/layout.test.ts | 6 ++--- app/tests/scene/lighting/sunDir.test.ts | 2 +- app/tests/scene/sky/sky.test.ts | 2 +- app/tests/scene/sky/skyConfig.test.ts | 2 +- app/tests/scene/trees/treePlacement.test.ts | 4 +-- app/tests/scene/trees/treeRenderer.test.ts | 6 ++--- .../trees/treeRendererCommitLookup.test.ts | 4 +-- app/tests/scene/world/worldBounds.test.ts | 2 +- app/tests/scene/worldLayoutCache.test.ts | 2 +- .../settings}/components/repoLabel.test.ts | 2 +- .../{config => state/settings}/drafts.test.ts | 0 .../settings}/footprint.test.ts | 2 +- .../{config => state/settings}/island.test.ts | 2 +- app/tests/views/panes/controlsPane.test.ts | 2 +- 96 files changed, 162 insertions(+), 138 deletions(-) rename app/src/{config => state/settings}/components/adPanels.ts (92%) rename app/src/{config => state/settings}/components/buildings.ts (99%) rename app/src/{config => state/settings}/components/facade.ts (98%) rename app/src/{config => state/settings}/components/fireflies.ts (97%) rename app/src/{config => state/settings}/components/footprint.ts (95%) rename app/src/{config => state/settings}/components/gem.ts (98%) rename app/src/{config => state/settings}/components/island.ts (95%) rename app/src/{config => state/settings}/components/lighting.ts (95%) rename app/src/{config => state/settings}/components/repoLabel.ts (96%) rename app/src/{config => state/settings}/components/sky.ts (94%) rename app/src/{config => state/settings}/components/streets.ts (98%) rename app/src/{config => state/settings}/components/trees.ts (98%) rename app/src/{config => state/settings}/effects/effects.ts (97%) rename app/src/{config => state/settings}/index.ts (97%) rename app/src/{config => state/settings}/prefs/liveUpdates.ts (94%) rename app/src/{config => state/settings}/prefs/syntaxTheme.ts (96%) rename app/src/{config => state/settings}/system/animator.ts (94%) rename app/src/{config => state/settings}/system/cameraRig.ts (97%) rename app/src/{config => state/settings}/system/inputHandlers.ts (88%) rename app/src/{config => state/settings}/system/tooltip.ts (81%) rename app/src/{config => state/settings}/world/world.ts (96%) rename app/tests/{config => state/settings}/components/repoLabel.test.ts (89%) rename app/tests/{config => state/settings}/drafts.test.ts (100%) rename app/tests/{config => state/settings}/footprint.test.ts (88%) rename app/tests/{config => state/settings}/island.test.ts (92%) diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index f6f17ef9..6bd27aca 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -25,7 +25,7 @@ import { buildCommitPane } from './views/panes/commitPane.js'; import { buildStreetPane } from './views/panes/streetPane.js'; import { sameDayCommitCount } from './utils/commitMetrics.js'; import { labelFromManifest } from './utils/sources.js'; -import { LIVE_UPDATES } from './config/index.js'; +import { LIVE_UPDATES } from './state/settings/index.js'; import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './store/liveStatus.js'; import { DateSource, NodeKind } from './types'; import type { DirNode, FileNode, PickTarget, TreeNode } from './types'; diff --git a/app/src/main.ts b/app/src/main.ts index 449c1ced..582dbf7f 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -4,10 +4,10 @@ import './styles.css'; -import * as Config from './config/index.js'; +import * as Config from './state/settings/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 { SYNTAX_THEME } from './state/settings/prefs/syntaxTheme.js'; import { sourceKey, CURRENT_SOURCE_KEY } from './store/sourceContext.js'; import { attachCommitReactions } from './store/configCommitReactions.js'; import { setupLiveUpdates } from './store/liveUpdates.js'; diff --git a/app/src/scene/components/adPanels/adPanelsInstanced.ts b/app/src/scene/components/adPanels/adPanelsInstanced.ts index c6508cbc..e34a8e50 100644 --- a/app/src/scene/components/adPanels/adPanelsInstanced.ts +++ b/app/src/scene/components/adPanels/adPanelsInstanced.ts @@ -12,7 +12,7 @@ import * as THREE from 'three'; import { BuildingOrient } from '@/types/index.js'; -import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/config/index.js'; +import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/state/settings/index.js'; import { RENDER_ORDERS } from '@/constants'; import { mediaKindOf, MediaKind } from './adPanels.js'; import { AdPanelTextureArray, MAX_PAGES as AD_PANEL_MAX_PAGES } from './adPanelTextureArray.js'; diff --git a/app/src/scene/components/buildings/buildingColor.ts b/app/src/scene/components/buildings/buildingColor.ts index 6c7133de..051f60df 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.js'; import { NodeKind } from '@/types'; // Structural shapes match what real Manifest tree / FileNode supply but diff --git a/app/src/scene/components/buildings/buildingTilt.ts b/app/src/scene/components/buildings/buildingTilt.ts index 5155ff8e..794be49c 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.js'; 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..9a1ef4f0 100644 --- a/app/src/scene/components/buildings/buildings.ts +++ b/app/src/scene/components/buildings/buildings.ts @@ -17,7 +17,7 @@ import { LIGHTING, SCENE_COLORS, WINDOW_LIGHTING, -} from '@/config/index.js'; +} from '@/state/settings/index.js'; import type { IconAtlas } from './iconAtlas.js'; import { writeSunDir } from '@/scene/components/lighting/sunDir.js'; diff --git a/app/src/scene/components/buildings/buildingsCell.ts b/app/src/scene/components/buildings/buildingsCell.ts index 3870f38b..46c17d1c 100644 --- a/app/src/scene/components/buildings/buildingsCell.ts +++ b/app/src/scene/components/buildings/buildingsCell.ts @@ -10,7 +10,7 @@ // index/position buffers). import * as THREE from 'three'; -import { FACADE_GEOMETRY } from '@/config/index.js'; +import { FACADE_GEOMETRY } from '@/state/settings/index.js'; import { BuildingOrient } from '@/types/index.js'; import buildingVertSrc from './building.vert.glsl?raw'; import buildingFragSrc from './building.frag.glsl?raw'; diff --git a/app/src/scene/components/fireflies/fireflies.ts b/app/src/scene/components/fireflies/fireflies.ts index 3323f42f..9fab05cc 100644 --- a/app/src/scene/components/fireflies/fireflies.ts +++ b/app/src/scene/components/fireflies/fireflies.ts @@ -8,7 +8,7 @@ import { createFireflyRenderer, type FireflyRenderer } from './firefliesRenderer import { createOrbitRings } from './orbitRings.js'; import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; import type { CommitEntry } from '@/types'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies.js'; /** 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..e904ccac 100644 --- a/app/src/scene/components/fireflies/firefliesPlacement.ts +++ b/app/src/scene/components/fireflies/firefliesPlacement.ts @@ -10,8 +10,8 @@ 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 { TREES } from '@/state/settings/components/trees.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies.js'; import { computeAgeRange, computeSizeRange, diff --git a/app/src/scene/components/fireflies/firefliesRenderer.ts b/app/src/scene/components/fireflies/firefliesRenderer.ts index 3c071f43..df764f66 100644 --- a/app/src/scene/components/fireflies/firefliesRenderer.ts +++ b/app/src/scene/components/fireflies/firefliesRenderer.ts @@ -9,7 +9,7 @@ import * as THREE from 'three'; import { RENDER_ORDERS } from '@/constants'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies.js'; import type { FireflyPlacement } from './firefliesPlacement.js'; 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..4c0a0297 100644 --- a/app/src/scene/components/fireflies/orbitRings.ts +++ b/app/src/scene/components/fireflies/orbitRings.ts @@ -24,8 +24,8 @@ // 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 { FIREFLIES } from '@/state/settings/components/fireflies.js'; +import { RAINBOW } from '@/state/settings/effects/effects.js'; import type { FireflyPlacement } from './firefliesPlacement.js'; const TUBULAR_SEGMENTS = 96; // segments around the loop diff --git a/app/src/scene/components/footprint/footprint.ts b/app/src/scene/components/footprint/footprint.ts index 8b4fc7f5..aa317d20 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.js'; 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..923dc9d9 100644 --- a/app/src/scene/components/gem/gem.ts +++ b/app/src/scene/components/gem/gem.ts @@ -11,7 +11,12 @@ // 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.js'; import { NodeKind } from '@/types'; import { gemAnchorXZ } from '../../utils/gemAnchor.js'; import type { Street } from '@/types'; diff --git a/app/src/scene/components/island/islandMesh.ts b/app/src/scene/components/island/islandMesh.ts index 804cdfa6..afe0226e 100644 --- a/app/src/scene/components/island/islandMesh.ts +++ b/app/src/scene/components/island/islandMesh.ts @@ -9,7 +9,7 @@ // island.dispose(); import * as THREE from 'three'; -import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/config/components/island.js'; +import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island.js'; import { getWorldBounds, type WorldBounds } from '@/scene/layout/worldBounds.js'; import { buildIslandGeometry, type IslandBuildParams } from './islandGeometry.js'; import { createIslandMaterial } from './islandShader.js'; diff --git a/app/src/scene/components/island/islandShader.ts b/app/src/scene/components/island/islandShader.ts index e17c46b6..8877da03 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.js'; 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..fc6e1e4e 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.js'; // --------------------------------------------------------------------------- // Public types diff --git a/app/src/scene/components/labels/labelsCell.ts b/app/src/scene/components/labels/labelsCell.ts index eaf0ee49..2e09c4fc 100644 --- a/app/src/scene/components/labels/labelsCell.ts +++ b/app/src/scene/components/labels/labelsCell.ts @@ -15,7 +15,7 @@ // 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.js'; import { RENDER_ORDERS } from '@/constants'; import type { Building } from '@/types/index.js'; import type { CellTile } from '../../layout/cellTile.js'; diff --git a/app/src/scene/components/lighting/sunDir.ts b/app/src/scene/components/lighting/sunDir.ts index fb6d889c..23fb8918 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.js'; 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..13d62a91 100644 --- a/app/src/scene/components/repoLabel/repoLabel.ts +++ b/app/src/scene/components/repoLabel/repoLabel.ts @@ -29,8 +29,8 @@ 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.js'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel.js'; import { RENDER_ORDERS } from '@/constants'; import vertSrc from './holoQuad.vert.glsl?raw'; diff --git a/app/src/scene/components/sky/sky.ts b/app/src/scene/components/sky/sky.ts index 65708757..157c9445 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.js'; +import { CAMERA_PERSPECTIVE } from '@/state/settings/system/cameraRig.js'; 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..4e5f5fdb 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.js'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind, StreetAxis } from '@/types'; import type { Street } from '@/types'; diff --git a/app/src/scene/components/streets/streets.ts b/app/src/scene/components/streets/streets.ts index b96d9010..57ba3e4b 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.js'; 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..41a84e62 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 { TREES } from '@/state/settings/components/trees.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; +import { ISLAND_GEOMETRY } from '@/state/settings/components/island.js'; import { getWorldBounds } from '../../layout/worldBounds.js'; import { buildTopPolygon, pointInIslandPolygon } from '../island/islandGeometry.js'; import { islandSeedFromBounds } from '../island/islandMesh.js'; import { StreetAxis } from '@/types'; import { gemAnchorXZ } from '../../utils/gemAnchor.js'; import type { Building, CityBbox, CityLayout, Street } from '@/types'; -import type { IslandGeometryConfig } from '@/config/components/island.js'; +import type { IslandGeometryConfig } from '@/state/settings/components/island.js'; interface Rect { minX: number; diff --git a/app/src/scene/components/trees/treePlacementClient.ts b/app/src/scene/components/trees/treePlacementClient.ts index b1c61af5..84dc2e40 100644 --- a/app/src/scene/components/trees/treePlacementClient.ts +++ b/app/src/scene/components/trees/treePlacementClient.ts @@ -8,11 +8,11 @@ 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 { TREES } from '@/state/settings/components/trees.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint.js'; +import { ISLAND_GEOMETRY } from '@/state/settings/components/island.js'; +import { WORLD } from '@/state/settings/world/world.js'; 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..475d8807 100644 --- a/app/src/scene/components/trees/treePlacementWorker.ts +++ b/app/src/scene/components/trees/treePlacementWorker.ts @@ -5,11 +5,11 @@ // 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 { TREES } from '@/state/settings/components/trees.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint.js'; +import { WORLD } from '@/state/settings/world/world.js'; +import type { IslandGeometryConfig } from '@/state/settings/components/island.js'; import type { CityBbox, CityLayout } from '@/types'; type TreesValue = ReturnType; diff --git a/app/src/scene/components/trees/treeRenderer.ts b/app/src/scene/components/trees/treeRenderer.ts index 225bc9a8..35a787b8 100644 --- a/app/src/scene/components/trees/treeRenderer.ts +++ b/app/src/scene/components/trees/treeRenderer.ts @@ -23,7 +23,7 @@ // 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.js'; import { RENDER_ORDERS } from '@/constants'; import type { TreePlacement } from './treePlacement.js'; import type { CommitEntry } from '@/types'; diff --git a/app/src/scene/effects/buildingFader.ts b/app/src/scene/effects/buildingFader.ts index 27a5cc1a..7f6224d2 100644 --- a/app/src/scene/effects/buildingFader.ts +++ b/app/src/scene/effects/buildingFader.ts @@ -15,7 +15,7 @@ // 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.js'; import { FadeDetail, NodeKind } from '@/types'; import type { DirNode, FileNode, PickTarget } from '@/types'; import { parentDirPath } from '@/scene/utils/path.js'; diff --git a/app/src/scene/effects/outlineRenderer.ts b/app/src/scene/effects/outlineRenderer.ts index f89b7a35..8c357aa4 100644 --- a/app/src/scene/effects/outlineRenderer.ts +++ b/app/src/scene/effects/outlineRenderer.ts @@ -21,7 +21,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 { BUILDING_OUTLINE, RAINBOW } from '@/config/index.js'; +import { BUILDING_OUTLINE, RAINBOW } from '@/state/settings/index.js'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind } from '@/types'; import { UNIT_BOX_EDGE_POSITIONS } from '@/scene/world.js'; diff --git a/app/src/scene/effects/pathLineRenderer.ts b/app/src/scene/effects/pathLineRenderer.ts index d8442cd7..ee0485e7 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.js'; /** * Converts a LINEWIDTH_PCT percentage (1–50) into an actual pixel linewidth diff --git a/app/src/scene/effects/treeOutlineRenderer.ts b/app/src/scene/effects/treeOutlineRenderer.ts index 87936e81..7574a81f 100644 --- a/app/src/scene/effects/treeOutlineRenderer.ts +++ b/app/src/scene/effects/treeOutlineRenderer.ts @@ -19,8 +19,8 @@ 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.js'; +import { RAINBOW } from '@/state/settings/effects/effects.js'; import { RENDER_ORDERS } from '@/constants'; import { NodeKind } from '@/types'; import { buildCanopyEdges } from '@/scene/components/trees/treeRenderer.js'; diff --git a/app/src/scene/layout/layout.ts b/app/src/scene/layout/layout.ts index bdf89cc8..9172be03 100644 --- a/app/src/scene/layout/layout.ts +++ b/app/src/scene/layout/layout.ts @@ -14,8 +14,13 @@ // 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.js'; +import type { StreetTier } from '@/state/settings/components/streets.js'; import { BuildingOrient, JoinSide, NodeKind, StreetAxis } from '@/types'; import type { Building, CityLayout, RangeStat, Street } from '@/types'; import { parentDirPath } from '../utils/path.js'; diff --git a/app/src/scene/layout/layoutClient.ts b/app/src/scene/layout/layoutClient.ts index 839cecf1..f3a2755d 100644 --- a/app/src/scene/layout/layoutClient.ts +++ b/app/src/scene/layout/layoutClient.ts @@ -15,7 +15,12 @@ // 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 { + STREET_LAYOUT, + BUILDING_DIMENSIONS, + GEM_SIZING, + STREET_TIERS, +} from '@/state/settings/index.js'; import { layoutCity, makeHeightContext, recomputeBuildingDimensions } from './layout.js'; import type { Manifest, CityLayout, FileNode, TreeNode } from '@/types'; diff --git a/app/src/scene/layout/layoutWorker.ts b/app/src/scene/layout/layoutWorker.ts index 108c16fc..705c3d25 100644 --- a/app/src/scene/layout/layoutWorker.ts +++ b/app/src/scene/layout/layoutWorker.ts @@ -4,7 +4,12 @@ // 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 { + STREET_LAYOUT, + BUILDING_DIMENSIONS, + GEM_SIZING, + STREET_TIERS, +} from '@/state/settings/index.js'; 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..6337dcc2 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.js'; /** 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 9e1173cb..96a2a1ef 100644 --- a/app/src/scene/renderLoop.ts +++ b/app/src/scene/renderLoop.ts @@ -16,11 +16,11 @@ import { GEM_GLOW, GEM_SIZING, BLOOM, -} from '../config/index.js'; +} from '../state/settings/index.js'; import { NodeKind, StreetAxis } from '../types'; import type { Manifest } from '../types'; -import { SKY } from '@/config/components/sky.js'; +import { SKY } from '@/state/settings/components/sky.js'; import { createWorld } from './world.js'; import { refreshBuildingMaterial } from './components/buildings/buildings.js'; import { createCameraRig } from './system/cameraRig.js'; diff --git a/app/src/scene/system/animator.ts b/app/src/scene/system/animator.ts index 69b8f735..3a481a1c 100644 --- a/app/src/scene/system/animator.ts +++ b/app/src/scene/system/animator.ts @@ -28,7 +28,7 @@ // cannot conflict by construction. import * as THREE from 'three'; -import { ANIMATION_TIMING } from '@/config/index.js'; +import { ANIMATION_TIMING } from '@/state/settings/index.js'; import type { Building, WorldDiff } from '@/types'; import type { createWorld } from '../world.js'; diff --git a/app/src/scene/system/cameraRig.ts b/app/src/scene/system/cameraRig.ts index ff380aa0..038dce31 100644 --- a/app/src/scene/system/cameraRig.ts +++ b/app/src/scene/system/cameraRig.ts @@ -26,7 +26,7 @@ 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 { CAMERA_PERSPECTIVE, CAMERA_CONTROLS, ANIMATION_TIMING } from '@/state/settings/index.js'; import { CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; import { StreetAxis } from '@/types'; import type { Building, Street } from '@/types'; diff --git a/app/src/scene/system/inputHandlers.ts b/app/src/scene/system/inputHandlers.ts index 3a1b9335..4520dc9f 100644 --- a/app/src/scene/system/inputHandlers.ts +++ b/app/src/scene/system/inputHandlers.ts @@ -10,7 +10,7 @@ // handlers.dispose(); import * as THREE from 'three'; -import { INPUT_TIMING } from '@/config/index.js'; +import { INPUT_TIMING } from '@/state/settings/index.js'; import { KEY_BINDINGS, TEXT_INPUT_TAGS } from '@/constants'; import { NodeKind } from '@/types'; import type { PickTarget } from '@/types'; diff --git a/app/src/scene/system/postFx.ts b/app/src/scene/system/postFx.ts index d737b757..c5e29cb6 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.js'; export interface PostFx { render(): void; diff --git a/app/src/scene/world.ts b/app/src/scene/world.ts index 77a1c2bb..ecec3e6b 100644 --- a/app/src/scene/world.ts +++ b/app/src/scene/world.ts @@ -62,14 +62,14 @@ 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 { FOOTPRINT } from '@/state/settings/components/footprint.js'; import { getBuildingColor, getCreatedAge, getModifiedAge, getDateRanges, } from './components/buildings/buildingColor.js'; -import { SKY } from '@/config/components/sky.js'; +import { SKY } from '@/state/settings/components/sky.js'; import { ASPHALT, GEM_APPEARANCE, @@ -80,7 +80,7 @@ import { TREES, SCENE_COLORS, SIDEWALK_COLORS, -} from '@/config/index.js'; +} from '@/state/settings/index.js'; import { REBUILD_STATUS } from '@/store/liveStatus.js'; import type { Building, 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 99% rename from app/src/config/components/buildings.ts rename to app/src/state/settings/components/buildings.ts index a5343d25..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 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 98% rename from app/src/config/components/gem.ts rename to app/src/state/settings/components/gem.ts index 0cc24cfe..b8aadc9b 100644 --- a/app/src/config/components/gem.ts +++ b/app/src/state/settings/components/gem.ts @@ -1,4 +1,4 @@ -// 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). 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 97% rename from app/src/config/index.ts rename to app/src/state/settings/index.ts index 553d74b2..4324c202 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: 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 81% rename from app/src/config/system/tooltip.ts rename to app/src/state/settings/system/tooltip.ts index eb32d90d..b35203af 100644 --- a/app/src/config/system/tooltip.ts +++ b/app/src/state/settings/system/tooltip.ts @@ -1,4 +1,4 @@ -// config/system/tooltip.ts — Tooltip placement. Consumed by +// 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/store/configCommitReactions.ts b/app/src/store/configCommitReactions.ts index e01fc1c2..e8042e12 100644 --- a/app/src/store/configCommitReactions.ts +++ b/app/src/store/configCommitReactions.ts @@ -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.js'; 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.js'; 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.js'; // 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/liveUpdates.ts b/app/src/store/liveUpdates.ts index 4bca6677..95161d94 100644 --- a/app/src/store/liveUpdates.ts +++ b/app/src/store/liveUpdates.ts @@ -13,7 +13,7 @@ // 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 { LIVE_UPDATES, POLL_SECONDS_MIN, POLL_SECONDS_MAX } from '@/state/settings/index.js'; import { REBUILD_STATUS, LAST_REBUILD_ERROR, setRefreshManifest } from '@/store/liveStatus.js'; import { streamManifest } from '@/api/manifest.js'; import { manifestUrl, signatureUrl } from '@/api/urls.js'; diff --git a/app/src/views/components/tooltip.ts b/app/src/views/components/tooltip.ts index 992be173..692d2ae1 100644 --- a/app/src/views/components/tooltip.ts +++ b/app/src/views/components/tooltip.ts @@ -4,7 +4,7 @@ // 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.js'; import { DOM_IDS } from '@/constants'; let _el: HTMLElement | null = null; diff --git a/app/src/views/panes/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 8c48d651..7a460ed9 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -47,17 +47,21 @@ 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'; +} from '@/state/settings/index.js'; +import { SKY, SKY_STARS } from '@/state/settings/components/sky.js'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel.js'; +import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island.js'; +import { WORLD } from '@/state/settings/world/world.js'; +import { TREES, TREE_OUTLINE } from '@/state/settings/components/trees.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies.js'; +import { FOOTPRINT } from '@/state/settings/components/footprint.js'; +import { + FACADE_GEOMETRY, + FACADE_DETAIL, + WINDOW_LIGHTING, +} from '@/state/settings/components/facade.js'; +import { AD_PANEL } from '@/state/settings/components/adPanels.js'; +import { ANIMATION_TIMING } from '@/state/settings/system/animator.js'; import { getDefault, forEachRegisteredStore, onAnyChange } from '@/store/persist.js'; import { setDraft, diff --git a/app/src/views/panes/filePreviewPane.ts b/app/src/views/panes/filePreviewPane.ts index 5aa6e825..b935c171 100644 --- a/app/src/views/panes/filePreviewPane.ts +++ b/app/src/views/panes/filePreviewPane.ts @@ -10,7 +10,7 @@ // 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/components/icon.js'; diff --git a/app/src/views/panes/streetPane.ts b/app/src/views/panes/streetPane.ts index adb315dc..e1f371e7 100644 --- a/app/src/views/panes/streetPane.ts +++ b/app/src/views/panes/streetPane.ts @@ -12,7 +12,7 @@ import type { DirNode, FileNode, TreeNode } from '@/types'; import { makeLucideIcon } from '@/views/components/icon.js'; import { buildPaneHeader } from '@/views/shell/paneHeader.js'; import { makeExtensionBadge } from '@/views/components/badge.js'; -import { ASPHALT, BUILDING_PALETTE } from '@/config'; +import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; interface BuildStreetPaneOpts { onClose?: () => void; diff --git a/app/src/views/shell/appHeader.ts b/app/src/views/shell/appHeader.ts index ee603c16..9bb0fde9 100644 --- a/app/src/views/shell/appHeader.ts +++ b/app/src/views/shell/appHeader.ts @@ -6,7 +6,7 @@ // // When no path is selected (or only the root), #app-title is empty. -import { ASPHALT, BUILDING_PALETTE } from '@/config'; +import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; import { makeGemIcon, makeLucideIcon } from '../components/icon.js'; import { makeExtensionBadge } from '../components/badge.js'; import { toHttpsRepoUrl } from '@/utils/sources.js'; diff --git a/app/tests/_helpers/cityFixtures.ts b/app/tests/_helpers/cityFixtures.ts index 8c9c6587..4271e6ec 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.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; /** 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/typography.ts b/app/tests/_helpers/typography.ts index 55cc826c..86cc34d3 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.js'; export const TYPOGRAPHY: LabelTypographyConfig = { FONT_FAMILY: 'sans-serif', diff --git a/app/tests/scene/cityFootprint/footprint.test.ts b/app/tests/scene/cityFootprint/footprint.test.ts index db2b45a2..5a65ac7f 100644 --- a/app/tests/scene/cityFootprint/footprint.test.ts +++ b/app/tests/scene/cityFootprint/footprint.test.ts @@ -2,7 +2,7 @@ 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 { FOOTPRINT } from '@/state/settings/components/footprint.js'; import { StreetAxis } from '@/types'; import type { CityLayout } from '@/types'; 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..fa51a536 100644 --- a/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts +++ b/app/tests/scene/components/adPanels/instanced-ad-panels.test.ts @@ -8,7 +8,7 @@ import { AdPanelTextureArray, PANEL_TEX_SIZE, } from '@/scene/components/adPanels/adPanelTextureArray.js'; -import { BLOOM } from '@/config/index.js'; +import { BLOOM } from '@/state/settings/index.js'; import { BuildingOrient, NodeKind } from '@/types/index.js'; import type { Building } from '@/types/index.js'; diff --git a/app/tests/scene/components/buildings/buildingColor.test.ts b/app/tests/scene/components/buildings/buildingColor.test.ts index d2609491..78d58a80 100644 --- a/app/tests/scene/components/buildings/buildingColor.test.ts +++ b/app/tests/scene/components/buildings/buildingColor.test.ts @@ -7,8 +7,8 @@ import { getBuildingColor, getModifiedAge, } from '@/scene/components/buildings/buildingColor.js'; -import { BUILDING_PALETTE } from '@/config/index.js'; -import type { BuildingPaletteConfig } from '@/config/components/buildings.js'; +import { BUILDING_PALETTE } from '@/state/settings/index.js'; +import type { BuildingPaletteConfig } from '@/state/settings/components/buildings.js'; import { NodeKind } from '@/types'; import type { RangeStat } from '@/types'; 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..1156a2d9 100644 --- a/app/tests/scene/components/labels/instanced-labels-cell.test.ts +++ b/app/tests/scene/components/labels/instanced-labels-cell.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, vi } from 'vitest'; import * as THREE from 'three'; -import { LABEL_TYPOGRAPHY } from '@/config/index.js'; +import { LABEL_TYPOGRAPHY } from '@/state/settings/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'; diff --git a/app/tests/scene/components/repoLabel/repoLabel.test.ts b/app/tests/scene/components/repoLabel/repoLabel.test.ts index c88b6248..7fbbc03f 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 { REPO_LABEL } from '@/state/settings/components/repoLabel.js'; 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..cbb29dbb 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 { REPO_LABEL } from '@/state/settings/components/repoLabel.js'; import { resetBuildingsConfig } from '../../../_helpers/cityFixtures'; // Positioning math below assumes BUILDING_DIMENSIONS.MAX_FLOORS=96, diff --git a/app/tests/scene/effects/buildingFader.test.ts b/app/tests/scene/effects/buildingFader.test.ts index 2a7449f5..b1c1289f 100644 --- a/app/tests/scene/effects/buildingFader.test.ts +++ b/app/tests/scene/effects/buildingFader.test.ts @@ -7,7 +7,7 @@ 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 { BUILDING_FADE } from '@/state/settings/index.js'; 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..1b0aa7d1 100644 --- a/app/tests/scene/effects/pathLineRenderer.test.ts +++ b/app/tests/scene/effects/pathLineRenderer.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, afterEach } from 'vitest'; -import { STREET_TIERS } from '@/config/index.js'; +import { STREET_TIERS } from '@/state/settings/index.js'; import { computePathLinewidthPixels } from '@/scene/effects/pathLineRenderer.js'; // Capture the original tiers so afterEach can restore them. diff --git a/app/tests/scene/effects/treeOutlineRenderer.test.ts b/app/tests/scene/effects/treeOutlineRenderer.test.ts index b44f42e4..899239f7 100644 --- a/app/tests/scene/effects/treeOutlineRenderer.test.ts +++ b/app/tests/scene/effects/treeOutlineRenderer.test.ts @@ -7,8 +7,8 @@ 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 { TREE_OUTLINE } from '@/state/settings/components/trees.js'; +import { RAINBOW } from '@/state/settings/effects/effects.js'; import { NodeKind } from '@/types'; import type { PickTarget } from '@/types/picker.js'; diff --git a/app/tests/scene/fireflies/fireflies.test.ts b/app/tests/scene/fireflies/fireflies.test.ts index a04075ec..fd1effd9 100644 --- a/app/tests/scene/fireflies/fireflies.test.ts +++ b/app/tests/scene/fireflies/fireflies.test.ts @@ -1,7 +1,7 @@ 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 { FIREFLIES } from '@/state/settings/components/fireflies.js'; import type { CommitEntry } from '@/types'; import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; diff --git a/app/tests/scene/fireflies/firefliesPlacement.test.ts b/app/tests/scene/fireflies/firefliesPlacement.test.ts index 34a4da38..730a2f74 100644 --- a/app/tests/scene/fireflies/firefliesPlacement.test.ts +++ b/app/tests/scene/fireflies/firefliesPlacement.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from 'vitest'; import { placeFireflies } from '@/scene/components/fireflies/firefliesPlacement.js'; -import { FIREFLIES } from '@/config/components/fireflies.js'; +import { FIREFLIES } from '@/state/settings/components/fireflies.js'; import type { CommitEntry } from '@/types'; import type { TreePlacement } from '@/scene/components/trees/treePlacement.js'; diff --git a/app/tests/scene/island/islandMesh.test.ts b/app/tests/scene/island/islandMesh.test.ts index 3182afea..1f0a1131 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 { ISLAND_GEOMETRY } from '@/state/settings/components/island.js'; import { RENDER_ORDERS } from '@/constants'; describe('createIsland', () => { diff --git a/app/tests/scene/layout/layout.test.ts b/app/tests/scene/layout/layout.test.ts index 32a768f0..d2b1efe1 100644 --- a/app/tests/scene/layout/layout.test.ts +++ b/app/tests/scene/layout/layout.test.ts @@ -8,10 +8,10 @@ import { __test, } from '@/scene/layout/layout.js'; import type { Rect } from '@/scene/layout/layout.js'; -import { BUILDING_DIMENSIONS } from '@/config/index.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/index.js'; 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.js'; +import type { StreetTier } from '@/state/settings/components/streets.js'; import { assertNoOverlap, assertStemOrder, diff --git a/app/tests/scene/lighting/sunDir.test.ts b/app/tests/scene/lighting/sunDir.test.ts index e45f7c58..80705c48 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 { LIGHTING } from '@/state/settings/components/lighting.js'; describe('writeSunDir', () => { beforeEach(() => { diff --git a/app/tests/scene/sky/sky.test.ts b/app/tests/scene/sky/sky.test.ts index 836f5e5d..cc4ae10e 100644 --- a/app/tests/scene/sky/sky.test.ts +++ b/app/tests/scene/sky/sky.test.ts @@ -7,7 +7,7 @@ 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 { SKY, SKY_STARS } from '@/state/settings/components/sky.js'; 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..deea9ffe 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.js'; describe('SKY', () => { it('exposes the expected keys with the right types', () => { diff --git a/app/tests/scene/trees/treePlacement.test.ts b/app/tests/scene/trees/treePlacement.test.ts index 40dc23f6..78d8c3d1 100644 --- a/app/tests/scene/trees/treePlacement.test.ts +++ b/app/tests/scene/trees/treePlacement.test.ts @@ -2,7 +2,7 @@ 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 { TREES } from '@/state/settings/components/trees.js'; 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/treeRenderer.test.ts b/app/tests/scene/trees/treeRenderer.test.ts index ea84517c..857fd1bc 100644 --- a/app/tests/scene/trees/treeRenderer.test.ts +++ b/app/tests/scene/trees/treeRenderer.test.ts @@ -11,9 +11,9 @@ 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 { TREES } from '@/state/settings/components/trees.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; +import { LIGHTING } from '@/state/settings/components/lighting.js'; import { RENDER_ORDERS } from '@/constants'; import type { CommitEntry } from '@/types'; import { commits as buildCommits } from './_commitFixtures.js'; diff --git a/app/tests/scene/trees/treeRendererCommitLookup.test.ts b/app/tests/scene/trees/treeRendererCommitLookup.test.ts index e661fded..1887f9cb 100644 --- a/app/tests/scene/trees/treeRendererCommitLookup.test.ts +++ b/app/tests/scene/trees/treeRendererCommitLookup.test.ts @@ -7,8 +7,8 @@ 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 { TREES } from '@/state/settings/components/trees.js'; +import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; import type { CommitEntry } from '@/types'; function resetStores() { diff --git a/app/tests/scene/world/worldBounds.test.ts b/app/tests/scene/world/worldBounds.test.ts index ffa4bcd6..87716f2a 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 { WORLD } from '@/state/settings/world/world.js'; 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..df3779a7 100644 --- a/app/tests/scene/worldLayoutCache.test.ts +++ b/app/tests/scene/worldLayoutCache.test.ts @@ -18,7 +18,7 @@ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; import { attachCommitReactions } from '@/store/configCommitReactions.js'; -import { STREET_LAYOUT } from '@/config/index.js'; +import { STREET_LAYOUT } from '@/state/settings/index.js'; describe('configCommitReactions invalidates layout cache before applyManifest', () => { let calls: string[]; diff --git a/app/tests/config/components/repoLabel.test.ts b/app/tests/state/settings/components/repoLabel.test.ts similarity index 89% rename from app/tests/config/components/repoLabel.test.ts rename to app/tests/state/settings/components/repoLabel.test.ts index 17dd5de6..a8062a48 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.js'; describe('REPO_LABEL config store', () => { beforeEach(() => { diff --git a/app/tests/config/drafts.test.ts b/app/tests/state/settings/drafts.test.ts similarity index 100% rename from app/tests/config/drafts.test.ts rename to app/tests/state/settings/drafts.test.ts 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..c87a5989 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.js'; 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..d4be7e7b 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.js'; describe('ISLAND config defaults', () => { it('GEOMETRY exposes the expected keys with the right types', () => { diff --git a/app/tests/views/panes/controlsPane.test.ts b/app/tests/views/panes/controlsPane.test.ts index 8081a473..cdbb9be0 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 * as Config from '@/state/settings/index.js'; describe('buildControlsPane', () => { it('returns a pane with the Settings header + Keyboard & mouse section first', () => { From 20ac7eb4bba63343524e0e2e0a3d8d7c9078de38 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 13:57:58 -0400 Subject: [PATCH 08/10] =?UTF-8?q?refactor:=20rename=20store/=20=E2=86=92?= =?UTF-8?q?=20state/=20with=20split?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Runtime atoms (liveStatus, liveUpdates, sourceContext) move to state/runtime/. Config infrastructure (drafts, persist, reactions) moves to state/ root next to state/settings/. The "config" prefix drops from drafts/reactions filenames since their parent already implies "settings infrastructure". After this and the config→settings rename, all reactive state lives under state/, with settings/ for persisted user-tunable and runtime/ for session-only. Co-Authored-By: Claude Opus 4.7 (1M context) --- app/src/coordinator.ts | 2 +- app/src/main.ts | 10 +++++----- app/src/scene/system/cameraRig.ts | 2 +- app/src/scene/world.ts | 2 +- app/src/{store/configDrafts.ts => state/drafts.ts} | 2 +- app/src/{store => state}/persist.ts | 4 ++-- .../configCommitReactions.ts => state/reactions.ts} | 4 ++-- app/src/{store => state/runtime}/liveStatus.ts | 8 ++++---- app/src/{store => state/runtime}/liveUpdates.ts | 8 ++++++-- app/src/{store => state/runtime}/sourceContext.ts | 2 +- app/src/views/panes/controlsPane.ts | 4 ++-- app/tests/scene/worldLayoutCache.test.ts | 2 +- app/tests/state/{settings => }/drafts.test.ts | 4 ++-- app/tests/{ => state}/persistPerSource.test.ts | 4 ++-- app/tests/{ => state/runtime}/sourceContext.test.ts | 2 +- app/tests/views/panes/controlsPane.test.ts | 2 +- 16 files changed, 33 insertions(+), 29 deletions(-) rename app/src/{store/configDrafts.ts => state/drafts.ts} (98%) rename app/src/{store => state}/persist.ts (98%) rename app/src/{store/configCommitReactions.ts => state/reactions.ts} (99%) rename app/src/{store => state/runtime}/liveStatus.ts (88%) rename app/src/{store => state/runtime}/liveUpdates.ts (96%) rename app/src/{store => state/runtime}/sourceContext.ts (92%) rename app/tests/state/{settings => }/drafts.test.ts (98%) rename app/tests/{ => state}/persistPerSource.test.ts (94%) rename app/tests/{ => state/runtime}/sourceContext.test.ts (91%) diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index 6bd27aca..ca51b2b0 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -26,7 +26,7 @@ import { buildStreetPane } from './views/panes/streetPane.js'; import { sameDayCommitCount } from './utils/commitMetrics.js'; import { labelFromManifest } from './utils/sources.js'; import { LIVE_UPDATES } from './state/settings/index.js'; -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.js'; import { DateSource, NodeKind } from './types'; import type { DirNode, FileNode, PickTarget, TreeNode } from './types'; import type { createWorld } from './scene/world.js'; diff --git a/app/src/main.ts b/app/src/main.ts index 582dbf7f..5de0141d 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -5,12 +5,12 @@ import './styles.css'; import * as Config from './state/settings/index.js'; -import { REBUILD_STATUS } from './store/liveStatus.js'; -import { attachPersistence, persistAtomPerSource } from './store/persist.js'; +import { REBUILD_STATUS } from './state/runtime/liveStatus.js'; +import { attachPersistence, persistAtomPerSource } from './state/persist.js'; import { SYNTAX_THEME } from './state/settings/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 { sourceKey, CURRENT_SOURCE_KEY } from './state/runtime/sourceContext.js'; +import { attachCommitReactions } from './state/reactions.js'; +import { setupLiveUpdates } from './state/runtime/liveUpdates.js'; import { DOM_IDS } from './constants'; import { NodeKind } from './types'; import type { Manifest } from './types'; diff --git a/app/src/scene/system/cameraRig.ts b/app/src/scene/system/cameraRig.ts index 038dce31..c957419b 100644 --- a/app/src/scene/system/cameraRig.ts +++ b/app/src/scene/system/cameraRig.ts @@ -27,7 +27,7 @@ import * as THREE from 'three'; import { OrbitControls } from 'three/addons/controls/OrbitControls.js'; import { CAMERA_PERSPECTIVE, CAMERA_CONTROLS, ANIMATION_TIMING } from '@/state/settings/index.js'; -import { CURRENT_SOURCE_KEY } from '@/store/sourceContext.js'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext.js'; import { StreetAxis } from '@/types'; import type { Building, Street } from '@/types'; import type { createWorld } from '../world.js'; diff --git a/app/src/scene/world.ts b/app/src/scene/world.ts index ecec3e6b..684de4ff 100644 --- a/app/src/scene/world.ts +++ b/app/src/scene/world.ts @@ -81,7 +81,7 @@ import { SCENE_COLORS, SIDEWALK_COLORS, } from '@/state/settings/index.js'; -import { REBUILD_STATUS } from '@/store/liveStatus.js'; +import { REBUILD_STATUS } from '@/state/runtime/liveStatus.js'; import type { Building, CityBbox, diff --git a/app/src/store/configDrafts.ts b/app/src/state/drafts.ts similarity index 98% rename from app/src/store/configDrafts.ts rename to app/src/state/drafts.ts index a05129e0..b66a9f68 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 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..c450f383 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.js'; // 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 99% rename from app/src/store/configCommitReactions.ts rename to app/src/state/reactions.ts index e8042e12..44c8804a 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.js'; import { // Rebuild-required (affects layout or geometry): 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 96% rename from app/src/store/liveUpdates.ts rename to app/src/state/runtime/liveUpdates.ts index 95161d94..514aedb5 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 + @@ -14,7 +14,11 @@ // "rebuilding…" indicator only lights up when there's real work. import { LIVE_UPDATES, POLL_SECONDS_MIN, POLL_SECONDS_MAX } from '@/state/settings/index.js'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, setRefreshManifest } from '@/store/liveStatus.js'; +import { + REBUILD_STATUS, + LAST_REBUILD_ERROR, + setRefreshManifest, +} from '@/state/runtime/liveStatus.js'; import { streamManifest } from '@/api/manifest.js'; import { manifestUrl, signatureUrl } from '@/api/urls.js'; import { _applyDisplayLabel, startRenderLoop } from '@/scene/renderLoop.js'; 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/panes/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 7a460ed9..9b9b55ca 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -62,7 +62,7 @@ import { } from '@/state/settings/components/facade.js'; import { AD_PANEL } from '@/state/settings/components/adPanels.js'; import { ANIMATION_TIMING } from '@/state/settings/system/animator.js'; -import { getDefault, forEachRegisteredStore, onAnyChange } from '@/store/persist.js'; +import { getDefault, forEachRegisteredStore, onAnyChange } from '@/state/persist.js'; import { setDraft, getEffective, @@ -72,7 +72,7 @@ import { discard as discardDrafts, isDirty as draftsAreDirty, subscribe as subscribeDrafts, -} from '@/store/configDrafts.js'; +} from '@/state/drafts.js'; import { KEY_BINDINGS } from '@/constants'; import { FadeDetail } from '@/types'; import { makeLucideIcon } from '@/views/components/icon.js'; diff --git a/app/tests/scene/worldLayoutCache.test.ts b/app/tests/scene/worldLayoutCache.test.ts index df3779a7..3f453744 100644 --- a/app/tests/scene/worldLayoutCache.test.ts +++ b/app/tests/scene/worldLayoutCache.test.ts @@ -17,7 +17,7 @@ // `["invalidateLayoutCache", "applyManifest"]`. Post-fix: passes. import { describe, it, expect, beforeEach, afterEach } from 'vitest'; -import { attachCommitReactions } from '@/store/configCommitReactions.js'; +import { attachCommitReactions } from '@/state/reactions.js'; import { STREET_LAYOUT } from '@/state/settings/index.js'; describe('configCommitReactions invalidates layout cache before applyManifest', () => { diff --git a/app/tests/state/settings/drafts.test.ts b/app/tests/state/drafts.test.ts similarity index 98% rename from app/tests/state/settings/drafts.test.ts rename to app/tests/state/drafts.test.ts index 5af68a6c..53e63263 100644 --- a/app/tests/state/settings/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.js'; +import { persistStore } from '@/state/persist.js'; 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..dabe73e7 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.js'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext.js'; describe('persistAtomPerSource', () => { beforeEach(() => { diff --git a/app/tests/sourceContext.test.ts b/app/tests/state/runtime/sourceContext.test.ts similarity index 91% rename from app/tests/sourceContext.test.ts rename to app/tests/state/runtime/sourceContext.test.ts index c713b86d..47868055 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.js'; describe('sourceKey', () => { it('is deterministic for the same (src, branch)', () => { diff --git a/app/tests/views/panes/controlsPane.test.ts b/app/tests/views/panes/controlsPane.test.ts index cdbb9be0..430cef3b 100644 --- a/app/tests/views/panes/controlsPane.test.ts +++ b/app/tests/views/panes/controlsPane.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect, beforeAll } from 'vitest'; import { buildControlsPane } from '@/views/panes/controlsPane.js'; -import { attachPersistence } from '@/store/persist.js'; +import { attachPersistence } from '@/state/persist.js'; import * as Config from '@/state/settings/index.js'; describe('buildControlsPane', () => { From 8618696a57534b08d9922df729a1caec04226ee0 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 14:25:55 -0400 Subject: [PATCH 09/10] refactor: drop .js import extensions + restructure api/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two cleanups bundled together since the .js sweep touched the api/ files: - api/urls.ts → api/index.ts. Now holds only buildApiUrl + BuildApiUrlOpts (the shared base helper). Endpoint-specific URL builders move next to their endpoint module. - manifestUrl + signatureUrl move from api/urls.ts → api/manifest.ts (alongside streamManifest). main.ts + state/runtime/liveUpdates.ts update their imports accordingly. - Drop the .js extension from every first-party import across app/src and app/tests. With moduleResolution: bundler + Vite, the extension was redundant; bare paths work the same. three/addons/... and three/examples/... imports KEEP their .js because those are external module exports that require the extension. Pure mechanical refactor: 151 files touched, all behavior preserved. tsc clean. 2013 tests pass. --- app/src/api/{urls.ts => index.ts} | 23 ++---- app/src/api/manifest.ts | 16 +++++ app/src/coordinator.ts | 28 ++++---- app/src/main.ts | 40 +++++------ .../components/adPanels/adPanelsInstanced.ts | 10 +-- .../components/buildings/buildingColor.ts | 2 +- .../components/buildings/buildingIndex.ts | 4 +- .../components/buildings/buildingTilt.ts | 2 +- .../scene/components/buildings/buildings.ts | 6 +- .../components/buildings/buildingsCell.ts | 14 ++-- .../scene/components/buildings/iconAtlas.ts | 2 +- .../scene/components/fireflies/authorColor.ts | 2 +- .../scene/components/fireflies/fireflies.ts | 10 +-- .../fireflies/firefliesPlacement.ts | 10 +-- .../components/fireflies/firefliesRenderer.ts | 4 +- .../scene/components/fireflies/orbitRings.ts | 6 +- .../scene/components/footprint/footprint.ts | 2 +- app/src/scene/components/gem/gem.ts | 4 +- app/src/scene/components/island/islandMesh.ts | 8 +-- .../scene/components/island/islandShader.ts | 2 +- app/src/scene/components/labels/labelAtlas.ts | 2 +- app/src/scene/components/labels/labels.ts | 4 +- app/src/scene/components/labels/labelsCell.ts | 8 +-- app/src/scene/components/lighting/sunDir.ts | 2 +- .../scene/components/repoLabel/repoLabel.ts | 6 +- app/src/scene/components/sky/sky.ts | 4 +- .../scene/components/streets/streetLabels.ts | 2 +- .../scene/components/streets/streetTile.ts | 2 +- app/src/scene/components/streets/streets.ts | 2 +- .../scene/components/trees/treePlacement.ts | 18 ++--- .../components/trees/treePlacementClient.ts | 14 ++-- .../components/trees/treePlacementWorker.ts | 14 ++-- .../scene/components/trees/treeRenderer.ts | 10 +-- app/src/scene/components/trees/trees.ts | 4 +- app/src/scene/effects/buildingFader.ts | 8 +-- app/src/scene/effects/ghostRenderer.ts | 4 +- app/src/scene/effects/outlineRenderer.ts | 10 +-- app/src/scene/effects/pathLineRenderer.ts | 8 +-- app/src/scene/effects/treeOutlineRenderer.ts | 8 +-- app/src/scene/layout/cellAssembly.ts | 14 ++-- app/src/scene/layout/cellTile.ts | 6 +- app/src/scene/layout/layout.ts | 12 ++-- app/src/scene/layout/layoutClient.ts | 4 +- app/src/scene/layout/layoutWorker.ts | 4 +- app/src/scene/layout/worldBounds.ts | 2 +- app/src/scene/renderLoop.ts | 36 +++++----- app/src/scene/system/animator.ts | 4 +- app/src/scene/system/cameraRig.ts | 6 +- app/src/scene/system/inputHandlers.ts | 8 +-- app/src/scene/system/postFx.ts | 2 +- .../scene/utils/color/registerShaderChunks.ts | 2 +- app/src/scene/world.ts | 72 +++++++++---------- app/src/state/drafts.ts | 2 +- app/src/state/persist.ts | 2 +- app/src/state/reactions.ts | 8 +-- app/src/state/runtime/liveUpdates.ts | 13 ++-- app/src/state/settings/components/gem.ts | 2 +- app/src/state/settings/index.ts | 40 +++++------ app/src/types/picker.ts | 4 +- app/src/views/components/badge.ts | 2 +- app/src/views/components/sourcePicker.ts | 2 +- app/src/views/components/tooltip.ts | 2 +- app/src/views/panes/commitPane.ts | 12 ++-- app/src/views/panes/controlsPane.ts | 30 ++++---- app/src/views/panes/filePreviewPane.ts | 6 +- app/src/views/panes/infoPane.ts | 4 +- app/src/views/panes/searchPane.ts | 4 +- app/src/views/panes/streetPane.ts | 6 +- app/src/views/panes/treePane.ts | 6 +- app/src/views/shell/appFooter.ts | 2 +- app/src/views/shell/appHeader.ts | 8 +-- app/src/views/shell/leftSidebar.ts | 10 +-- app/src/views/shell/paneHeader.ts | 2 +- app/tests/_helpers/cityFixtures.ts | 4 +- app/tests/_helpers/layoutAsserts.ts | 4 +- app/tests/_helpers/typography.ts | 2 +- app/tests/api/config.test.ts | 2 +- app/tests/api/{urls.test.ts => index.test.ts} | 2 +- .../scene/cityFootprint/footprint.test.ts | 4 +- app/tests/scene/cityScene-collision.test.ts | 6 +- .../scene/cityScene-stem-diagnostic.test.ts | 10 +-- .../adPanels/instanced-ad-panels.test.ts | 10 +-- .../buildings/buildingColor.test.ts | 6 +- .../buildings/buildingIndex.test.ts | 6 +- .../instanced-buildings-cell.test.ts | 10 +-- .../labels/instanced-labels-atlas.test.ts | 4 +- .../labels/instanced-labels-cell.test.ts | 12 ++-- .../components/repoLabel/repoLabel.test.ts | 4 +- .../repoLabel/repoLabelPositioning.test.ts | 4 +- .../components/repoLabel/textCanvas.test.ts | 2 +- .../components/streets/streetTile.test.ts | 4 +- app/tests/scene/effects/buildingFader.test.ts | 4 +- .../scene/effects/pathLineRenderer.test.ts | 4 +- .../scene/effects/treeOutlineRenderer.test.ts | 8 +-- app/tests/scene/fireflies/authorColor.test.ts | 2 +- app/tests/scene/fireflies/fireflies.test.ts | 6 +- .../fireflies/firefliesPlacement.test.ts | 6 +- app/tests/scene/fireflies/orbitRings.test.ts | 4 +- app/tests/scene/island/islandGeometry.test.ts | 2 +- app/tests/scene/island/islandMesh.test.ts | 4 +- app/tests/scene/island/islandShader.test.ts | 2 +- app/tests/scene/layout/cellAssembly.test.ts | 4 +- app/tests/scene/layout/cellTile.test.ts | 4 +- app/tests/scene/layout/layout.test.ts | 10 +-- app/tests/scene/layout/layoutClient.test.ts | 2 +- app/tests/scene/layout/layoutPacker.test.ts | 2 +- app/tests/scene/layout/layoutTrace.test.ts | 8 +-- app/tests/scene/layout/spatialGrid.test.ts | 2 +- app/tests/scene/layout/worldOccupancy.test.ts | 2 +- app/tests/scene/lighting/fogChunk.test.ts | 2 +- app/tests/scene/lighting/sunDir.test.ts | 4 +- app/tests/scene/sky/sky.test.ts | 4 +- app/tests/scene/sky/skyConfig.test.ts | 2 +- app/tests/scene/system/cameraRig.test.ts | 2 +- app/tests/scene/system/picker-commit.test.ts | 2 +- app/tests/scene/system/picker.test.ts | 2 +- app/tests/scene/trees/treeEncoding.test.ts | 4 +- app/tests/scene/trees/treePlacement.test.ts | 4 +- .../scene/trees/treePlacementClient.test.ts | 2 +- app/tests/scene/trees/treeRenderer.test.ts | 12 ++-- .../trees/treeRendererCommitLookup.test.ts | 8 +-- .../scene/utils/color/colorInterp.test.ts | 2 +- .../scene/utils/color/hsl-glsl-parity.test.ts | 2 +- app/tests/scene/utils/color/hsl.test.ts | 2 +- app/tests/scene/utils/path.test.ts | 2 +- app/tests/scene/world/worldBounds.test.ts | 4 +- app/tests/scene/worldLayoutCache.test.ts | 4 +- app/tests/state/drafts.test.ts | 4 +- app/tests/state/persistPerSource.test.ts | 4 +- app/tests/state/runtime/sourceContext.test.ts | 2 +- .../settings/components/repoLabel.test.ts | 2 +- app/tests/state/settings/footprint.test.ts | 2 +- app/tests/state/settings/island.test.ts | 2 +- app/tests/utils/commitMetrics.test.ts | 2 +- app/tests/utils/commitUrl.test.ts | 2 +- app/tests/utils/dates.test.ts | 2 +- app/tests/utils/sourceRecents.test.ts | 2 +- app/tests/utils/sources.test.ts | 2 +- .../views/components/loadingOverlay.test.ts | 2 +- .../views/components/sourcePicker.test.ts | 4 +- app/tests/views/panes/commitPane.test.ts | 2 +- app/tests/views/panes/controlsPane.test.ts | 6 +- app/tests/views/panes/filePreviewPane.test.ts | 4 +- app/tests/views/panes/searchPane.test.ts | 2 +- app/tests/views/panes/streetPane.test.ts | 2 +- app/tests/views/panes/treePane.test.ts | 2 +- app/tests/views/shell/appHeader.test.ts | 2 +- app/tests/views/shell/badge.test.ts | 2 +- app/tests/views/shell/fileIcon.test.ts | 2 +- app/tests/views/shell/leftSidebar.test.ts | 2 +- app/tests/views/shell/tooltip.test.ts | 2 +- 151 files changed, 479 insertions(+), 481 deletions(-) rename app/src/api/{urls.ts => index.ts} (52%) rename app/tests/api/{urls.test.ts => index.test.ts} (96%) diff --git a/app/src/api/urls.ts b/app/src/api/index.ts similarity index 52% rename from app/src/api/urls.ts rename to app/src/api/index.ts index 841c44f1..a9209055 100644 --- a/app/src/api/urls.ts +++ b/app/src/api/index.ts @@ -1,6 +1,6 @@ -// api/urls.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/api/manifest.ts b/app/src/api/manifest.ts index 124ae8da..3299442e 100644 --- a/app/src/api/manifest.ts +++ b/app/src/api/manifest.ts @@ -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 ca51b2b0..c3922149 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 './utils/commitMetrics.js'; -import { labelFromManifest } from './utils/sources.js'; -import { LIVE_UPDATES } from './state/settings/index.js'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './state/runtime/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/commitMetrics'; +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 5de0141d..bd0ce031 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -4,30 +4,30 @@ import './styles.css'; -import * as Config from './state/settings/index.js'; -import { REBUILD_STATUS } from './state/runtime/liveStatus.js'; -import { attachPersistence, persistAtomPerSource } from './state/persist.js'; -import { SYNTAX_THEME } from './state/settings/prefs/syntaxTheme.js'; -import { sourceKey, CURRENT_SOURCE_KEY } from './state/runtime/sourceContext.js'; -import { attachCommitReactions } from './state/reactions.js'; -import { setupLiveUpdates } from './state/runtime/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 './api/urls.js'; -import { srcKind, labelFromUrl } from './utils/sources.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/components/sourcePicker.js'; -import { createLoadingOverlay } from './views/components/loadingOverlay.js'; -import { streamManifest } from './api/manifest.js'; -import { pushRecent } from './utils/sourceRecents.js'; -import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js'; -import { getServerConfig } from './api/config.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 './utils/sourceRecents'; +import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop'; +import { getServerConfig } from './api/config'; /** * Set document.title to "{label} (pending) — codecity" from a server-emitted diff --git a/app/src/scene/components/adPanels/adPanelsInstanced.ts b/app/src/scene/components/adPanels/adPanelsInstanced.ts index e34a8e50..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 '@/state/settings/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 051f60df..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 '@/state/settings/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 794be49c..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 '@/state/settings/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 9a1ef4f0..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 '@/state/settings/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 46c17d1c..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 '@/state/settings/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/components/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 e5f807c0..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/components/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 9fab05cc..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 '@/state/settings/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 e904ccac..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 '@/state/settings/components/trees.js'; -import { FIREFLIES } from '@/state/settings/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 df764f66..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 '@/state/settings/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 4c0a0297..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 '@/state/settings/components/fireflies.js'; -import { RAINBOW } from '@/state/settings/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 aa317d20..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 '@/state/settings/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 923dc9d9..954d4b3b 100644 --- a/app/src/scene/components/gem/gem.ts +++ b/app/src/scene/components/gem/gem.ts @@ -16,9 +16,9 @@ import { GEM_FACE_PALETTE, GEM_APPEARANCE, GEM_GLOW, -} from '@/state/settings/components/gem.js'; +} 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 afe0226e..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 '@/state/settings/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 8877da03..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 '@/state/settings/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 fc6e1e4e..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 '@/state/settings/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 2e09c4fc..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 '@/state/settings/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 23fb8918..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 '@/state/settings/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 13d62a91..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 '@/state/settings/components/buildings.js'; -import { REPO_LABEL } from '@/state/settings/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 157c9445..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 '@/state/settings/components/sky.js'; -import { CAMERA_PERSPECTIVE } from '@/state/settings/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 4e5f5fdb..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 '@/state/settings/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 57ba3e4b..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 '@/state/settings/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 41a84e62..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 '@/state/settings/components/trees.js'; -import { FOOTPRINT } from '@/state/settings/components/footprint.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; -import { ISLAND_GEOMETRY } from '@/state/settings/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 '@/state/settings/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 84dc2e40..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 '@/state/settings/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; -import { FOOTPRINT } from '@/state/settings/components/footprint.js'; -import { ISLAND_GEOMETRY } from '@/state/settings/components/island.js'; -import { WORLD } from '@/state/settings/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 475d8807..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 '@/state/settings/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; -import { FOOTPRINT } from '@/state/settings/components/footprint.js'; -import { WORLD } from '@/state/settings/world/world.js'; -import type { IslandGeometryConfig } from '@/state/settings/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 35a787b8..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 '@/state/settings/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 7f6224d2..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 '@/state/settings/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 8c357aa4..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 '@/state/settings/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 ee0485e7..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 '@/state/settings/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 7574a81f..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 '@/state/settings/components/trees.js'; -import { RAINBOW } from '@/state/settings/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 9172be03..21a0969d 100644 --- a/app/src/scene/layout/layout.ts +++ b/app/src/scene/layout/layout.ts @@ -19,14 +19,14 @@ import { BUILDING_DIMENSIONS, STREET_LAYOUT, GEM_SIZING, -} from '@/state/settings/index.js'; -import type { StreetTier } from '@/state/settings/components/streets.js'; +} 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 f3a2755d..b42295ec 100644 --- a/app/src/scene/layout/layoutClient.ts +++ b/app/src/scene/layout/layoutClient.ts @@ -20,8 +20,8 @@ import { BUILDING_DIMENSIONS, GEM_SIZING, STREET_TIERS, -} from '@/state/settings/index.js'; -import { layoutCity, makeHeightContext, recomputeBuildingDimensions } from './layout.js'; +} 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 705c3d25..64b8bd4a 100644 --- a/app/src/scene/layout/layoutWorker.ts +++ b/app/src/scene/layout/layoutWorker.ts @@ -3,13 +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 { layoutCity } from './layout'; import { STREET_LAYOUT, BUILDING_DIMENSIONS, GEM_SIZING, STREET_TIERS, -} from '@/state/settings/index.js'; +} 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 6337dcc2..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 '@/state/settings/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 96a2a1ef..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 '../state/settings/index.js'; +} from '../state/settings/index'; import { NodeKind, StreetAxis } from '../types'; import type { Manifest } from '../types'; -import { SKY } from '@/state/settings/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/components/tooltip.js'; -import { createPostFx } from './system/postFx.js'; -import { registerRenderer as registerAdPanelRenderer } from './components/adPanels/adPanelTextureArray.js'; -import { labelFromManifest } from '@/utils/sources.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 3a481a1c..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 '@/state/settings/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 c957419b..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 '@/state/settings/index.js'; -import { CURRENT_SOURCE_KEY } from '@/state/runtime/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 4520dc9f..567f0cee 100644 --- a/app/src/scene/system/inputHandlers.ts +++ b/app/src/scene/system/inputHandlers.ts @@ -10,13 +10,13 @@ // handlers.dispose(); import * as THREE from 'three'; -import { INPUT_TIMING } from '@/state/settings/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 c5e29cb6..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 '@/state/settings/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 684de4ff..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 '@/state/settings/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 '@/state/settings/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 '@/state/settings/index.js'; -import { REBUILD_STATUS } from '@/state/runtime/liveStatus.js'; +} from '@/state/settings/index'; +import { REBUILD_STATUS } from '@/state/runtime/liveStatus'; import type { Building, CityBbox, diff --git a/app/src/state/drafts.ts b/app/src/state/drafts.ts index b66a9f68..92291cf6 100644 --- a/app/src/state/drafts.ts +++ b/app/src/state/drafts.ts @@ -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/state/persist.ts b/app/src/state/persist.ts index c450f383..d6d991ca 100644 --- a/app/src/state/persist.ts +++ b/app/src/state/persist.ts @@ -17,7 +17,7 @@ import type { WritableAtom } from 'nanostores'; import { STORAGE_PREFIX, STORAGE_PER_SOURCE_PREFIX } from '@/constants'; -import { CURRENT_SOURCE_KEY } from '@/state/runtime/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/state/reactions.ts b/app/src/state/reactions.ts index 44c8804a..a64ba97a 100644 --- a/app/src/state/reactions.ts +++ b/app/src/state/reactions.ts @@ -18,7 +18,7 @@ import { listenKeys } from 'nanostores'; -import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from '@/state/runtime/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 '@/state/settings/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 '@/state/settings/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 '@/state/settings/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/state/runtime/liveUpdates.ts b/app/src/state/runtime/liveUpdates.ts index 514aedb5..a6ecb7b3 100644 --- a/app/src/state/runtime/liveUpdates.ts +++ b/app/src/state/runtime/liveUpdates.ts @@ -13,15 +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 '@/state/settings/index.js'; -import { - REBUILD_STATUS, - LAST_REBUILD_ERROR, - setRefreshManifest, -} from '@/state/runtime/liveStatus.js'; -import { streamManifest } from '@/api/manifest.js'; -import { manifestUrl, signatureUrl } from '@/api/urls.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/state/settings/components/gem.ts b/app/src/state/settings/components/gem.ts index b8aadc9b..e30921f1 100644 --- a/app/src/state/settings/components/gem.ts +++ b/app/src/state/settings/components/gem.ts @@ -3,7 +3,7 @@ // 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/state/settings/index.ts b/app/src/state/settings/index.ts index 4324c202..9605799f 100644 --- a/app/src/state/settings/index.ts +++ b/app/src/state/settings/index.ts @@ -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/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/views/components/badge.ts b/app/src/views/components/badge.ts index 74d27d7c..d28c38dd 100644 --- a/app/src/views/components/badge.ts +++ b/app/src/views/components/badge.ts @@ -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/components/sourcePicker.ts b/app/src/views/components/sourcePicker.ts index a0ebc5e8..90449156 100644 --- a/app/src/views/components/sourcePicker.ts +++ b/app/src/views/components/sourcePicker.ts @@ -2,7 +2,7 @@ // 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 '@/utils/sourceRecents.js'; +import { listRecents, removeRecent } from '@/utils/sourceRecents'; import { LUCIDE_ICON_BASE_URL } from '@/constants'; // ── Hosting-site SVG icons ─────────────────────────────────────────────────── diff --git a/app/src/views/components/tooltip.ts b/app/src/views/components/tooltip.ts index 692d2ae1..922773ff 100644 --- a/app/src/views/components/tooltip.ts +++ b/app/src/views/components/tooltip.ts @@ -4,7 +4,7 @@ // object has a brief name label so the city feels alive without forcing // a sidebar open. -import { TOOLTIP } from '@/state/settings/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 5fea0b18..64ab1d79 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/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { commitUrl } from '@/utils/commitUrl.js'; -import { formatRelativeAge, formatFullDate } from '@/utils/dates.js'; -import { fetchCommitDetail } from '@/api/commit.js'; -import { colorForAuthor } from '@/scene/components/fireflies/authorColor.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/paneHeader'; +import { commitUrl } from '@/utils/commitUrl'; +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/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 9b9b55ca..643e58bb 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -47,22 +47,22 @@ import { SYNTAX_THEME, SYNTAX_THEME_DEFAULT, SYNTAX_THEME_OPTIONS, -} from '@/state/settings/index.js'; -import { SKY, SKY_STARS } from '@/state/settings/components/sky.js'; -import { REPO_LABEL } from '@/state/settings/components/repoLabel.js'; -import { ISLAND_GEOMETRY, ISLAND_MATERIALS } from '@/state/settings/components/island.js'; -import { WORLD } from '@/state/settings/world/world.js'; -import { TREES, TREE_OUTLINE } from '@/state/settings/components/trees.js'; -import { FIREFLIES } from '@/state/settings/components/fireflies.js'; -import { FOOTPRINT } from '@/state/settings/components/footprint.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.js'; -import { AD_PANEL } from '@/state/settings/components/adPanels.js'; -import { ANIMATION_TIMING } from '@/state/settings/system/animator.js'; -import { getDefault, forEachRegisteredStore, onAnyChange } from '@/state/persist.js'; +} 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, @@ -72,11 +72,11 @@ import { discard as discardDrafts, isDirty as draftsAreDirty, subscribe as subscribeDrafts, -} from '@/state/drafts.js'; +} from '@/state/drafts'; import { KEY_BINDINGS } from '@/constants'; import { FadeDetail } from '@/types'; -import { makeLucideIcon } from '@/views/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/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 b935c171..84f3ae9e 100644 --- a/app/src/views/panes/filePreviewPane.ts +++ b/app/src/views/panes/filePreviewPane.ts @@ -13,9 +13,9 @@ import hljs from 'highlight.js/lib/common'; import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; import { PreviewKind } from '@/types'; import type { FileNode } from '@/types'; -import { makeLucideIcon } from '@/views/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/components/badge.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/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 f4eae776..ddbcbe31 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/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/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 36c73a42..483daf93 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/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/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 e1f371e7..acd86fba 100644 --- a/app/src/views/panes/streetPane.ts +++ b/app/src/views/panes/streetPane.ts @@ -9,9 +9,9 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, TreeNode } from '@/types'; -import { makeLucideIcon } from '@/views/components/icon.js'; -import { buildPaneHeader } from '@/views/shell/paneHeader.js'; -import { makeExtensionBadge } from '@/views/components/badge.js'; +import { makeLucideIcon } from '@/views/components/icon'; +import { buildPaneHeader } from '@/views/shell/paneHeader'; +import { makeExtensionBadge } from '@/views/components/badge'; import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; interface BuildStreetPaneOpts { diff --git a/app/src/views/panes/treePane.ts b/app/src/views/panes/treePane.ts index 5282f279..00b3f60e 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/components/icon.js'; -import { makeFileIcon, makeFolderIcon } from '@/views/components/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/shell/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 9bb0fde9..96e96077 100644 --- a/app/src/views/shell/appHeader.ts +++ b/app/src/views/shell/appHeader.ts @@ -7,10 +7,10 @@ // When no path is selected (or only the root), #app-title is empty. import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; -import { makeGemIcon, makeLucideIcon } from '../components/icon.js'; -import { makeExtensionBadge } from '../components/badge.js'; -import { toHttpsRepoUrl } from '@/utils/sources.js'; -import { fitSegments } from '../components/pathTruncate.js'; +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 826e6d3b..e7b4c0fc 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 '@/utils/localFlag.js'; +import { loadFlag, saveFlag } from '@/utils/localFlag'; const SIDEBAR_MIN_WIDTH = 280; const SIDEBAR_MAX_WIDTH = 600; diff --git a/app/src/views/shell/paneHeader.ts b/app/src/views/shell/paneHeader.ts index 7b105b1d..a6ef7296 100644 --- a/app/src/views/shell/paneHeader.ts +++ b/app/src/views/shell/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 '../components/icon.js'; +import { makeLucideIcon } from '../components/icon'; interface BuildPaneHeaderOpts { /** Initial title text. Update later via the returned api.setTitle. */ diff --git a/app/tests/_helpers/cityFixtures.ts b/app/tests/_helpers/cityFixtures.ts index 4271e6ec..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 '@/state/settings/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/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 86cc34d3..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 '@/state/settings/index.js'; +import type { LabelTypographyConfig } from '@/state/settings/index'; export const TYPOGRAPHY: LabelTypographyConfig = { FONT_FAMILY: 'sans-serif', diff --git a/app/tests/api/config.test.ts b/app/tests/api/config.test.ts index 50d466e2..e7da1278 100644 --- a/app/tests/api/config.test.ts +++ b/app/tests/api/config.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach, vi } from 'vitest'; -import { fetchServerConfig, getServerConfig, _resetServerConfigForTests } from '@/api/config.js'; +import { fetchServerConfig, getServerConfig, _resetServerConfigForTests } from '@/api/config'; describe('fetchServerConfig', () => { beforeEach(() => { diff --git a/app/tests/api/urls.test.ts b/app/tests/api/index.test.ts similarity index 96% rename from app/tests/api/urls.test.ts rename to app/tests/api/index.test.ts index 9e83a0e9..6450d791 100644 --- a/app/tests/api/urls.test.ts +++ b/app/tests/api/index.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { buildApiUrl } from '@/api/urls.js'; +import { buildApiUrl } from '@/api'; describe('buildApiUrl', () => { it('forwards src param when present', () => { diff --git a/app/tests/scene/cityFootprint/footprint.test.ts b/app/tests/scene/cityFootprint/footprint.test.ts index 5a65ac7f..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 '@/state/settings/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 fa51a536..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 '@/state/settings/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 78d58a80..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 '@/state/settings/index.js'; -import type { BuildingPaletteConfig } from '@/state/settings/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 1156a2d9..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 '@/state/settings/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 7fbbc03f..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 '@/state/settings/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 cbb29dbb..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 '@/state/settings/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 b1c1289f..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 '@/state/settings/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 1b0aa7d1..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 '@/state/settings/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 899239f7..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 '@/state/settings/components/trees.js'; -import { RAINBOW } from '@/state/settings/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 fd1effd9..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 '@/state/settings/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 730a2f74..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 '@/state/settings/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 1f0a1131..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 '@/state/settings/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 d2b1efe1..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 '@/state/settings/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 '@/state/settings/components/buildings.js'; -import type { StreetTier } from '@/state/settings/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 80705c48..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 '@/state/settings/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 cc4ae10e..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 '@/state/settings/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 deea9ffe..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 '@/state/settings/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 78d8c3d1..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 '@/state/settings/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, 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 857fd1bc..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 '@/state/settings/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/components/buildings.js'; -import { LIGHTING } from '@/state/settings/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 1887f9cb..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 '@/state/settings/components/trees.js'; -import { BUILDING_DIMENSIONS } from '@/state/settings/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 87716f2a..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 '@/state/settings/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 3f453744..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 '@/state/reactions.js'; -import { STREET_LAYOUT } from '@/state/settings/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/state/drafts.test.ts b/app/tests/state/drafts.test.ts index 53e63263..44bb5064 100644 --- a/app/tests/state/drafts.test.ts +++ b/app/tests/state/drafts.test.ts @@ -10,8 +10,8 @@ import { isDirty, subscribe, _resetForTests, -} from '@/state/drafts.js'; -import { persistStore } from '@/state/persist.js'; +} from '@/state/drafts'; +import { persistStore } from '@/state/persist'; interface FooConfig { COLOR: string; diff --git a/app/tests/state/persistPerSource.test.ts b/app/tests/state/persistPerSource.test.ts index dabe73e7..ec24ec48 100644 --- a/app/tests/state/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 '@/state/persist.js'; -import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext.js'; +import { persistAtomPerSource } from '@/state/persist'; +import { CURRENT_SOURCE_KEY } from '@/state/runtime/sourceContext'; describe('persistAtomPerSource', () => { beforeEach(() => { diff --git a/app/tests/state/runtime/sourceContext.test.ts b/app/tests/state/runtime/sourceContext.test.ts index 47868055..bc0579e3 100644 --- a/app/tests/state/runtime/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 '@/state/runtime/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/state/settings/components/repoLabel.test.ts b/app/tests/state/settings/components/repoLabel.test.ts index a8062a48..20682e57 100644 --- a/app/tests/state/settings/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 '@/state/settings/components/repoLabel.js'; +import { REPO_LABEL } from '@/state/settings/components/repoLabel'; describe('REPO_LABEL config store', () => { beforeEach(() => { diff --git a/app/tests/state/settings/footprint.test.ts b/app/tests/state/settings/footprint.test.ts index c87a5989..db717a68 100644 --- a/app/tests/state/settings/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 '@/state/settings/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/state/settings/island.test.ts b/app/tests/state/settings/island.test.ts index d4be7e7b..433e252b 100644 --- a/app/tests/state/settings/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 '@/state/settings/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/utils/commitMetrics.test.ts b/app/tests/utils/commitMetrics.test.ts index cc7137b6..0923a15b 100644 --- a/app/tests/utils/commitMetrics.test.ts +++ b/app/tests/utils/commitMetrics.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { sameDayCommitCount } from '@/utils/commitMetrics.js'; +import { sameDayCommitCount } from '@/utils/commitMetrics'; import type { CommitEntry } from '@/types'; function commit(date: string, sha: string, files = 1): CommitEntry { diff --git a/app/tests/utils/commitUrl.test.ts b/app/tests/utils/commitUrl.test.ts index 9ffa2f4b..b8b09bf9 100644 --- a/app/tests/utils/commitUrl.test.ts +++ b/app/tests/utils/commitUrl.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { commitUrl } from '@/utils/commitUrl.js'; +import { commitUrl } from '@/utils/commitUrl'; describe('commitUrl', () => { it('appends /commit/ for a github URL', () => { 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/utils/sourceRecents.test.ts b/app/tests/utils/sourceRecents.test.ts index 23d56f00..2a31c0a2 100644 --- a/app/tests/utils/sourceRecents.test.ts +++ b/app/tests/utils/sourceRecents.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect, beforeEach } from 'vitest'; -import { listRecents, pushRecent, removeRecent } from '@/utils/sourceRecents.js'; +import { listRecents, pushRecent, removeRecent } from '@/utils/sourceRecents'; describe('sourceRecents', () => { beforeEach(() => { diff --git a/app/tests/utils/sources.test.ts b/app/tests/utils/sources.test.ts index 95439b3c..cbd8580b 100644 --- a/app/tests/utils/sources.test.ts +++ b/app/tests/utils/sources.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'vitest'; -import { labelFromManifest, labelFromUrl, srcKind, toHttpsRepoUrl } from '@/utils/sources.js'; +import { labelFromManifest, labelFromUrl, srcKind, toHttpsRepoUrl } from '@/utils/sources'; import type { Manifest } from '@/types/manifest'; // Test helper: build the minimal Manifest shape labelFromManifest reads, diff --git a/app/tests/views/components/loadingOverlay.test.ts b/app/tests/views/components/loadingOverlay.test.ts index dde2a32b..728e206b 100644 --- a/app/tests/views/components/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/components/loadingOverlay.js'; +import { createLoadingOverlay } from '@/views/components/loadingOverlay'; function mountRoot(): HTMLElement { document.body.innerHTML = ''; diff --git a/app/tests/views/components/sourcePicker.test.ts b/app/tests/views/components/sourcePicker.test.ts index 30de024b..8840a1f4 100644 --- a/app/tests/views/components/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/components/sourcePicker.js'; -import { pushRecent } from '@/utils/sourceRecents.js'; +import { createSourcePicker } from '@/views/components/sourcePicker'; +import { pushRecent } from '@/utils/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 430cef3b..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 '@/state/persist.js'; -import * as Config from '@/state/settings/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 f513392a..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/components/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 a35b4ff9..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/components/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 120a08c1..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/components/tooltip.js'; +import { showTooltip, hideTooltip, moveTooltip } from '@/views/components/tooltip'; describe('tooltip', () => { afterEach(() => { From 217a2e68c9164a4f9cd296c04f156d8360f0dcc2 Mon Sep 17 00:00:00 2001 From: Thalida Noel Date: Sat, 30 May 2026 14:40:36 -0400 Subject: [PATCH 10/10] =?UTF-8?q?refactor:=20thematic=20merges=20+=20state?= =?UTF-8?q?/runtime=20grouping=20+=20paneHeader=20=E2=86=92=20components?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three coherent regroupings on top of the Phase 1 reorg: - utils/commitUrl + utils/commitMetrics → utils/commit.ts (single commit-related helpers module). Tests merged into utils/commit.test.ts. - localFlag + sourceRecents move into state/runtime/ — sourceRecents is parallel to sourceContext (both are source-related runtime state); localFlag holds runtime UI flags too. state/runtime/ now owns ALL runtime state, persisted or not. - views/shell/paneHeader → views/components/paneHeader. paneHeader is a reusable element used by every pane — it belongs in components/, not in shell/ (which is reserved for page-level chrome: header, footer, sidebars). The "two persistence systems" smell (localFlag + sourceRecents do direct localStorage writes outside the persist.ts pipeline) is deferred to its own follow-up phase — that's an architectural change, not a file move. --- app/src/coordinator.ts | 2 +- app/src/main.ts | 2 +- app/src/{utils => state/runtime}/localFlag.ts | 2 +- .../{utils => state/runtime}/sourceRecents.ts | 4 +-- app/src/utils/commit.ts | 25 +++++++++++++++ app/src/utils/commitMetrics.ts | 9 ------ app/src/utils/commitUrl.ts | 12 ------- .../views/{shell => components}/paneHeader.ts | 0 app/src/views/components/sourcePicker.ts | 2 +- app/src/views/panes/commitPane.ts | 4 +-- app/src/views/panes/controlsPane.ts | 2 +- app/src/views/panes/filePreviewPane.ts | 2 +- app/src/views/panes/infoPane.ts | 2 +- app/src/views/panes/searchPane.ts | 2 +- app/src/views/panes/streetPane.ts | 2 +- app/src/views/panes/treePane.ts | 2 +- app/src/views/shell/leftSidebar.ts | 2 +- .../runtime}/sourceRecents.test.ts | 2 +- .../{commitUrl.test.ts => commit.test.ts} | 31 ++++++++++++++++++- app/tests/utils/commitMetrics.test.ts | 31 ------------------- .../views/components/sourcePicker.test.ts | 2 +- 21 files changed, 72 insertions(+), 70 deletions(-) rename app/src/{utils => state/runtime}/localFlag.ts (95%) rename app/src/{utils => state/runtime}/sourceRecents.ts (93%) create mode 100644 app/src/utils/commit.ts delete mode 100644 app/src/utils/commitMetrics.ts delete mode 100644 app/src/utils/commitUrl.ts rename app/src/views/{shell => components}/paneHeader.ts (100%) rename app/tests/{utils => state/runtime}/sourceRecents.test.ts (96%) rename app/tests/utils/{commitUrl.test.ts => commit.test.ts} (50%) delete mode 100644 app/tests/utils/commitMetrics.test.ts diff --git a/app/src/coordinator.ts b/app/src/coordinator.ts index c3922149..4898891a 100644 --- a/app/src/coordinator.ts +++ b/app/src/coordinator.ts @@ -23,7 +23,7 @@ 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/commitMetrics'; +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'; diff --git a/app/src/main.ts b/app/src/main.ts index bd0ce031..38a872f2 100644 --- a/app/src/main.ts +++ b/app/src/main.ts @@ -25,7 +25,7 @@ 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 './utils/sourceRecents'; +import { pushRecent } from './state/runtime/sourceRecents'; import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop'; import { getServerConfig } from './api/config'; diff --git a/app/src/utils/localFlag.ts b/app/src/state/runtime/localFlag.ts similarity index 95% rename from app/src/utils/localFlag.ts rename to app/src/state/runtime/localFlag.ts index c94c0637..e51e2a2d 100644 --- a/app/src/utils/localFlag.ts +++ b/app/src/state/runtime/localFlag.ts @@ -1,4 +1,4 @@ -// utils/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/utils/sourceRecents.ts b/app/src/state/runtime/sourceRecents.ts similarity index 93% rename from app/src/utils/sourceRecents.ts rename to app/src/state/runtime/sourceRecents.ts index e7dd24f4..a22ec432 100644 --- a/app/src/utils/sourceRecents.ts +++ b/app/src/state/runtime/sourceRecents.ts @@ -1,5 +1,5 @@ -// utils/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/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/commitMetrics.ts b/app/src/utils/commitMetrics.ts deleted file mode 100644 index 10c5b8c8..00000000 --- a/app/src/utils/commitMetrics.ts +++ /dev/null @@ -1,9 +0,0 @@ -// utils/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/src/utils/commitUrl.ts b/app/src/utils/commitUrl.ts deleted file mode 100644 index 6e894509..00000000 --- a/app/src/utils/commitUrl.ts +++ /dev/null @@ -1,12 +0,0 @@ -// utils/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/shell/paneHeader.ts b/app/src/views/components/paneHeader.ts similarity index 100% rename from app/src/views/shell/paneHeader.ts rename to app/src/views/components/paneHeader.ts diff --git a/app/src/views/components/sourcePicker.ts b/app/src/views/components/sourcePicker.ts index 90449156..9b81cb96 100644 --- a/app/src/views/components/sourcePicker.ts +++ b/app/src/views/components/sourcePicker.ts @@ -2,7 +2,7 @@ // 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 '@/utils/sourceRecents'; +import { listRecents, removeRecent } from '@/state/runtime/sourceRecents'; import { LUCIDE_ICON_BASE_URL } from '@/constants'; // ── Hosting-site SVG icons ─────────────────────────────────────────────────── diff --git a/app/src/views/panes/commitPane.ts b/app/src/views/panes/commitPane.ts index 64ab1d79..7280fd7d 100644 --- a/app/src/views/panes/commitPane.ts +++ b/app/src/views/panes/commitPane.ts @@ -16,8 +16,8 @@ import type { CommitEntry } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; -import { commitUrl } from '@/utils/commitUrl'; +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'; diff --git a/app/src/views/panes/controlsPane.ts b/app/src/views/panes/controlsPane.ts index 643e58bb..f2ab45e3 100644 --- a/app/src/views/panes/controlsPane.ts +++ b/app/src/views/panes/controlsPane.ts @@ -76,7 +76,7 @@ import { import { KEY_BINDINGS } from '@/constants'; import { FadeDetail } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +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 84f3ae9e..b6329d7b 100644 --- a/app/src/views/panes/filePreviewPane.ts +++ b/app/src/views/panes/filePreviewPane.ts @@ -14,7 +14,7 @@ import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; import { PreviewKind } from '@/types'; import type { FileNode } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +import { buildPaneHeader } from '@/views/components/paneHeader'; import { makeExtensionBadge } from '@/views/components/badge'; // Binary-unit thresholds for human-readable file size formatting. diff --git a/app/src/views/panes/infoPane.ts b/app/src/views/panes/infoPane.ts index ddbcbe31..5d529eac 100644 --- a/app/src/views/panes/infoPane.ts +++ b/app/src/views/panes/infoPane.ts @@ -8,7 +8,7 @@ import { marked } from 'marked'; import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +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 483daf93..0d0a8e0d 100644 --- a/app/src/views/panes/searchPane.ts +++ b/app/src/views/panes/searchPane.ts @@ -18,7 +18,7 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, Manifest, TreeNode } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +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 acd86fba..8ae3e85b 100644 --- a/app/src/views/panes/streetPane.ts +++ b/app/src/views/panes/streetPane.ts @@ -10,7 +10,7 @@ import { NodeKind } from '@/types'; import type { DirNode, FileNode, TreeNode } from '@/types'; import { makeLucideIcon } from '@/views/components/icon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +import { buildPaneHeader } from '@/views/components/paneHeader'; import { makeExtensionBadge } from '@/views/components/badge'; import { ASPHALT, BUILDING_PALETTE } from '@/state/settings'; diff --git a/app/src/views/panes/treePane.ts b/app/src/views/panes/treePane.ts index 00b3f60e..4b19d121 100644 --- a/app/src/views/panes/treePane.ts +++ b/app/src/views/panes/treePane.ts @@ -7,7 +7,7 @@ import { NodeKind } from '@/types'; import type { DirNode, Manifest, TreeNode } from '@/types'; import { makeGemIcon, makeLucideIcon } from '@/views/components/icon'; import { makeFileIcon, makeFolderIcon } from '@/views/components/fileIcon'; -import { buildPaneHeader } from '@/views/shell/paneHeader'; +import { buildPaneHeader } from '@/views/components/paneHeader'; interface TreeCtx { byPath: Record; diff --git a/app/src/views/shell/leftSidebar.ts b/app/src/views/shell/leftSidebar.ts index e7b4c0fc..29936a77 100644 --- a/app/src/views/shell/leftSidebar.ts +++ b/app/src/views/shell/leftSidebar.ts @@ -12,7 +12,7 @@ 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 '@/utils/localFlag'; +import { loadFlag, saveFlag } from '@/state/runtime/localFlag'; const SIDEBAR_MIN_WIDTH = 280; const SIDEBAR_MAX_WIDTH = 600; diff --git a/app/tests/utils/sourceRecents.test.ts b/app/tests/state/runtime/sourceRecents.test.ts similarity index 96% rename from app/tests/utils/sourceRecents.test.ts rename to app/tests/state/runtime/sourceRecents.test.ts index 2a31c0a2..066e393f 100644 --- a/app/tests/utils/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 '@/utils/sourceRecents'; +import { listRecents, pushRecent, removeRecent } from '@/state/runtime/sourceRecents'; describe('sourceRecents', () => { beforeEach(() => { diff --git a/app/tests/utils/commitUrl.test.ts b/app/tests/utils/commit.test.ts similarity index 50% rename from app/tests/utils/commitUrl.test.ts rename to app/tests/utils/commit.test.ts index b8b09bf9..1acf4878 100644 --- a/app/tests/utils/commitUrl.test.ts +++ b/app/tests/utils/commit.test.ts @@ -1,5 +1,6 @@ import { describe, it, expect } from 'vitest'; -import { commitUrl } from '@/utils/commitUrl'; +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/commitMetrics.test.ts b/app/tests/utils/commitMetrics.test.ts deleted file mode 100644 index 0923a15b..00000000 --- a/app/tests/utils/commitMetrics.test.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { describe, it, expect } from 'vitest'; -import { sameDayCommitCount } from '@/utils/commitMetrics'; -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); - }); -}); diff --git a/app/tests/views/components/sourcePicker.test.ts b/app/tests/views/components/sourcePicker.test.ts index 8840a1f4..97601ff2 100644 --- a/app/tests/views/components/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/components/sourcePicker'; -import { pushRecent } from '@/utils/sourceRecents'; +import { pushRecent } from '@/state/runtime/sourceRecents'; function mountRoot(): HTMLElement { document.body.innerHTML = '';