Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
2 changes: 1 addition & 1 deletion app/src/utils/serverConfig.ts → app/src/api/config.ts
Original file line number Diff line number Diff line change
@@ -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
Expand Down
23 changes: 7 additions & 16 deletions app/src/utils/url.ts → app/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// url.ts — Build API URLs from the page's query params. Pure function so
// it's directly unit-testable; main.ts wraps it with the live
// `window.location.*` values for runtime callers.
// api/index.ts — Shared API helpers. Endpoint-specific code (URL builders,
// fetchers, streaming) lives in sibling files (api/manifest.ts,
// api/commit.ts, api/config.ts).

export interface BuildApiUrlOpts {
noCache?: boolean;
Expand All @@ -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,
Expand All @@ -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);
}
18 changes: 17 additions & 1 deletion app/src/utils/manifestStream.ts → app/src/api/manifest.ts
Original file line number Diff line number Diff line change
@@ -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.
//
Expand All @@ -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.
Expand Down
28 changes: 14 additions & 14 deletions app/src/coordinator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@
// });
// coord.dispose();

import { initAppHeader } from './views/shell/appHeader.js';
import { initAppFooter } from './views/shell/appFooter.js';
import { showLeftSidebar } from './views/shell/leftSidebar.js';
import { showRightSidebar, hideRightSidebar } from './views/shell/rightSidebar.js';
import { buildFilePreviewPane, humanLanguageFor } from './views/panes/filePreviewPane.js';
import { buildCommitPane } from './views/panes/commitPane.js';
import { buildStreetPane } from './views/panes/streetPane.js';
import { sameDayCommitCount } from './views/widgets/commitMetrics.js';
import { labelFromManifest } from './views/widgets/displayLabel.js';
import { LIVE_UPDATES } from './config/index.js';
import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './store/liveStatus.js';
import { initAppHeader } from './views/shell/appHeader';
import { initAppFooter } from './views/shell/appFooter';
import { showLeftSidebar } from './views/shell/leftSidebar';
import { showRightSidebar, hideRightSidebar } from './views/shell/rightSidebar';
import { buildFilePreviewPane, humanLanguageFor } from './views/panes/filePreviewPane';
import { buildCommitPane } from './views/panes/commitPane';
import { buildStreetPane } from './views/panes/streetPane';
import { sameDayCommitCount } from './utils/commit';
import { labelFromManifest } from './utils/sources';
import { LIVE_UPDATES } from './state/settings/index';
import { REBUILD_STATUS, LAST_REBUILD_ERROR, LAST_UPDATED_AT } from './state/runtime/liveStatus';
import { DateSource, NodeKind } from './types';
import type { DirNode, FileNode, PickTarget, TreeNode } from './types';
import type { createWorld } from './scene/world.js';
import type { createPicker } from './scene/system/picker.js';
import type { createCameraRig } from './scene/system/cameraRig.js';
import type { createWorld } from './scene/world';
import type { createPicker } from './scene/system/picker';
import type { createCameraRig } from './scene/system/cameraRig';

interface CoordinatorOpts {
world: ReturnType<typeof createWorld>;
Expand Down
55 changes: 27 additions & 28 deletions app/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,30 @@

import './styles.css';

import * as Config from './config/index.js';
import { REBUILD_STATUS } from './store/liveStatus.js';
import { attachPersistence, persistAtomPerSource } from './store/persist.js';
import { SYNTAX_THEME } from './config/prefs/syntaxTheme.js';
import { sourceKey, CURRENT_SOURCE_KEY } from './store/sourceContext.js';
import { attachCommitReactions } from './store/configCommitReactions.js';
import { setupLiveUpdates } from './store/liveUpdates.js';
import * as Config from './state/settings/index';
import { REBUILD_STATUS } from './state/runtime/liveStatus';
import { attachPersistence, persistAtomPerSource } from './state/persist';
import { SYNTAX_THEME } from './state/settings/prefs/syntaxTheme';
import { sourceKey, CURRENT_SOURCE_KEY } from './state/runtime/sourceContext';
import { attachCommitReactions } from './state/reactions';
import { setupLiveUpdates } from './state/runtime/liveUpdates';
import { DOM_IDS } from './constants';
import { NodeKind } from './types';
import type { Manifest } from './types';

import { PICKER_SELECTION_KEY } from './scene/system/picker.js';
import { manifestUrl } from './utils/url.js';
import { _srcKind, _deriveLabel } from './utils/source.js';
import { applyHljsTheme } from './utils/syntaxTheme.js';
import { buildIconAtlas } from './scene/components/buildings/iconAtlas.js';
import { setIconAtlas } from './scene/components/buildings/buildings.js';
import { setCellIconAtlas } from './scene/components/buildings/buildingsCell.js';
import { createSourcePicker, type SourcePayload } from './views/source/sourcePicker.js';
import { createLoadingOverlay } from './views/source/loadingOverlay.js';
import { streamManifest } from './utils/manifestStream.js';
import { pushRecent } from './views/source/sourceRecents.js';
import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop.js';
import { labelFromUrl } from './views/widgets/displayLabel.js';
import { getServerConfig } from './utils/serverConfig.js';
import { PICKER_SELECTION_KEY } from './scene/system/picker';
import { manifestUrl } from './api/manifest';
import { srcKind, labelFromUrl } from './utils/sources';
import { applyHljsTheme } from './utils/syntaxTheme';
import { buildIconAtlas } from './scene/components/buildings/iconAtlas';
import { setIconAtlas } from './scene/components/buildings/buildings';
import { setCellIconAtlas } from './scene/components/buildings/buildingsCell';
import { createSourcePicker, type SourcePayload } from './views/components/sourcePicker';
import { createLoadingOverlay } from './views/components/loadingOverlay';
import { streamManifest } from './api/manifest';
import { pushRecent } from './state/runtime/sourceRecents';
import { startRenderLoop, _applyDisplayLabel } from './scene/renderLoop';
import { getServerConfig } from './api/config';

/**
* Set document.title to "{label} (pending) — codecity" from a server-emitted
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -383,15 +382,15 @@ 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);
pushRecent({
src: payload.src,
branch: resolvedBranch,
branchIsDefault,
label: _deriveLabel(payload.src),
label: labelFromUrl(payload.src) ?? payload.src,
});

if (!liveUpdatesStarted) {
Expand Down
10 changes: 5 additions & 5 deletions app/src/scene/components/adPanels/adPanelsInstanced.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@
// require WebGL2.

import * as THREE from 'three';
import { BuildingOrient } from '@/types/index.js';
import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/config/index.js';
import { BuildingOrient } from '@/types/index';
import { AD_PANEL, BLOOM, BUILDING_DIMENSIONS } from '@/state/settings/index';
import { RENDER_ORDERS } from '@/constants';
import { mediaKindOf, MediaKind } from './adPanels.js';
import { AdPanelTextureArray, MAX_PAGES as AD_PANEL_MAX_PAGES } from './adPanelTextureArray.js';
import type { Building } from '@/types/index.js';
import { mediaKindOf, MediaKind } from './adPanels';
import { AdPanelTextureArray, MAX_PAGES as AD_PANEL_MAX_PAGES } from './adPanelTextureArray';
import type { Building } from '@/types/index';

import adPanelVertSrc from './adPanel.vert.glsl?raw';
import adPanelFragSrc from './adPanel.frag.glsl?raw';
Expand Down
2 changes: 1 addition & 1 deletion app/src/scene/components/buildings/buildingColor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
// Tunables come from BUILDING_PALETTE in config/building.ts. Tests
// mutate the store via .setKey() in setup + restore in teardown.

import { BUILDING_PALETTE } from '@/config/components/buildings.js';
import { BUILDING_PALETTE } from '@/state/settings/components/buildings';
import { NodeKind } from '@/types';

// Structural shapes match what real Manifest tree / FileNode supply but
Expand Down
4 changes: 2 additions & 2 deletions app/src/scene/components/buildings/buildingIndex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string, Building>();
Expand Down
2 changes: 1 addition & 1 deletion app/src/scene/components/buildings/buildingTilt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
// or an inverse-shear ray transform (picker).

import * as THREE from 'three';
import { BUILDING_AGING } from '@/config/components/buildings.js';
import { BUILDING_AGING } from '@/state/settings/components/buildings';
import type { Building } from '@/types';

/**
Expand Down
6 changes: 3 additions & 3 deletions app/src/scene/components/buildings/buildings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ import {
LIGHTING,
SCENE_COLORS,
WINDOW_LIGHTING,
} from '@/config/index.js';
import type { IconAtlas } from './iconAtlas.js';
import { writeSunDir } from '@/scene/components/lighting/sunDir.js';
} from '@/state/settings/index';
import type { IconAtlas } from './iconAtlas';
import { writeSunDir } from '@/scene/components/lighting/sunDir';

// ---------------------------------------------------------------------------
// Per-instance facade attributes (window column count + door width) are
Expand Down
14 changes: 7 additions & 7 deletions app/src/scene/components/buildings/buildingsCell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@
// index/position buffers).

import * as THREE from 'three';
import { FACADE_GEOMETRY } from '@/config/index.js';
import { BuildingOrient } from '@/types/index.js';
import { FACADE_GEOMETRY } from '@/state/settings/index';
import { BuildingOrient } from '@/types/index';
import buildingVertSrc from './building.vert.glsl?raw';
import buildingFragSrc from './building.frag.glsl?raw';
import type { CellTile } from '../../layout/cellTile.js';
import type { Building } from '@/types/index.js';
import type { IconAtlas } from './iconAtlas.js';
import { getFileIconName } from '@/views/widgets/fileIcon.js';
import { seedFromPath, attachLeanAwareRaycast } from './buildingTilt.js';
import type { CellTile } from '../../layout/cellTile';
import type { Building } from '@/types/index';
import type { IconAtlas } from './iconAtlas';
import { getFileIconName } from '@/views/components/fileIcon';
import { seedFromPath, attachLeanAwareRaycast } from './buildingTilt';

// ---------------------------------------------------------------------------
// Shared geometry — unit box, constructed once at module load and
Expand Down
2 changes: 1 addition & 1 deletion app/src/scene/components/buildings/iconAtlas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import * as THREE from 'three';
import { NodeKind } from '@/types';
import type { DirNode, FileNode, Manifest, TreeNode } from '@/types';
import { FILE_ICON_CDN_BASE, getFileIconName } from '@/views/widgets/fileIcon.js';
import { FILE_ICON_CDN_BASE, getFileIconName } from '@/views/components/fileIcon';

// Atlas is 2048×2048 with 128-px slots → up to 16×16 = 256 unique
// icons at 4× the per-icon resolution of the old 64-px slots.
Expand Down
2 changes: 1 addition & 1 deletion app/src/scene/components/fireflies/authorColor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
10 changes: 5 additions & 5 deletions app/src/scene/components/fireflies/fireflies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
// lifecycle handle. Mirrors trees/trees.ts.

import * as THREE from 'three';
import { placeFireflies, type FireflyPlacement } from './firefliesPlacement.js';
import { createFireflyRenderer, type FireflyRenderer } from './firefliesRenderer.js';
import { createOrbitRings } from './orbitRings.js';
import type { TreePlacement } from '@/scene/components/trees/treePlacement.js';
import { placeFireflies, type FireflyPlacement } from './firefliesPlacement';
import { createFireflyRenderer, type FireflyRenderer } from './firefliesRenderer';
import { createOrbitRings } from './orbitRings';
import type { TreePlacement } from '@/scene/components/trees/treePlacement';
import type { CommitEntry } from '@/types';
import { FIREFLIES } from '@/config/components/fireflies.js';
import { FIREFLIES } from '@/state/settings/components/fireflies';

/** Public handle returned by createFireflies. Extends the renderer with
* sha-based hover/select methods so callers don't need to manage the
Expand Down
10 changes: 5 additions & 5 deletions app/src/scene/components/fireflies/firefliesPlacement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@
// encoding functions as treeRenderer, and read config defaults from TREES.

import type { CommitEntry } from '@/types';
import type { TreePlacement } from '@/scene/components/trees/treePlacement.js';
import { TREES } from '@/config/components/trees.js';
import { FIREFLIES } from '@/config/components/fireflies.js';
import type { TreePlacement } from '@/scene/components/trees/treePlacement';
import { TREES } from '@/state/settings/components/trees';
import { FIREFLIES } from '@/state/settings/components/fireflies';
import {
computeAgeRange,
computeSizeRange,
ageT,
sizeT,
} from '@/scene/components/trees/treeEncoding.js';
import { colorForAuthor, lightColorForAuthor } from './authorColor.js';
} from '@/scene/components/trees/treeEncoding';
import { colorForAuthor, lightColorForAuthor } from './authorColor';

export interface FireflyPlacement {
/** Orbit center, world X. */
Expand Down
4 changes: 2 additions & 2 deletions app/src/scene/components/fireflies/firefliesRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@

import * as THREE from 'three';
import { RENDER_ORDERS } from '@/constants';
import { FIREFLIES } from '@/config/components/fireflies.js';
import type { FireflyPlacement } from './firefliesPlacement.js';
import { FIREFLIES } from '@/state/settings/components/fireflies';
import type { FireflyPlacement } from './firefliesPlacement';
import vertexShader from './fireflies.vert.glsl?raw';
import fragmentShader from './fireflies.frag.glsl?raw';

Expand Down
6 changes: 3 additions & 3 deletions app/src/scene/components/fireflies/orbitRings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@
// dispose(): drop both slots' geometries + materials.

import * as THREE from 'three';
import { FIREFLIES } from '@/config/components/fireflies.js';
import { RAINBOW } from '@/config/effects/effects.js';
import type { FireflyPlacement } from './firefliesPlacement.js';
import { FIREFLIES } from '@/state/settings/components/fireflies';
import { RAINBOW } from '@/state/settings/effects/effects';
import type { FireflyPlacement } from './firefliesPlacement';

const TUBULAR_SEGMENTS = 96; // segments around the loop
const RADIAL_SEGMENTS = 6; // segments around the tube's cross-section
Expand Down
2 changes: 1 addition & 1 deletion app/src/scene/components/footprint/footprint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
// refresh() handles COLOR, CORNER_RADIUS, and ENABLED only.

import * as THREE from 'three';
import { FOOTPRINT } from '@/config/components/footprint.js';
import { FOOTPRINT } from '@/state/settings/components/footprint';
import { RENDER_ORDERS } from '@/constants';
import { StreetAxis } from '@/types';
import type { Building, CityLayout, Street } from '@/types';
Expand Down
Loading