========================================== 3DSL Viewer – 初期スケルトン(空の正式APIのみ)

以下、viewer/ にそのまま配置してよい。

viewer/ runtime/ bootstrapViewer.js viewerHub.js core/ cameraEngine.js frameController.js selectionController.js modeController.js microController.js visibilityController.js uiState.js structIndex.js renderer/ context.js ui/ pointerInput.js keyboardInput.js gizmo.js picker.js timeline.js

すべて 空ロジック・APIだけにしてある。 ここから実装を Codex なり ChatGPT なりにやらせれば、構造が絶対に崩れない。

========================================== 📁 runtime/bootstrapViewer.js

// runtime/bootstrapViewer.js import { createUiState } from ’./core/uiState.js’; import { buildUUIDIndex, detectFrameRange } from ’./core/structIndex.js’; import { CameraEngine } from ’./core/cameraEngine.js’; import { createFrameController } from ’./core/frameController.js’; import { createSelectionController } from ’./core/selectionController.js’; import { createModeController } from ’./core/modeController.js’; import { createMicroController } from ’./core/microController.js’; import { createVisibilityController } from ’./core/visibilityController.js’; import { createRendererContext } from ’./renderer/context.js’; import { createViewerHub } from ’./viewerHub.js’;

export function bootstrapViewer(canvasOrId, threeDSS, options = {}) { // 後で実装 }

export async function bootstrapViewerFromUrl(canvasOrId, url, options = {}) { // 後で実装 }

========================================== 📁 runtime/viewerHub.js

// runtime/viewerHub.js export function createViewerHub({ core, renderer }) {

const hub = { start() {}, stop() {}, pickObjectAt: (ndcX, ndcY) => {},

core: {
  frame: {
    set: (n) => {},
    get: () => {},
    step: (d) => {},
    range: () => {},
    startPlayback: () => {},
    stopPlayback: () => {},
  },
  selection: {
    select: (uuid) => {},
    clear: () => {},
    get: () => {},
  },
  camera: {
    rotate: (dTheta, dPhi) => {},
    pan: (dx, dy) => {},
    zoom: (delta) => {},
    reset: () => {},
    snapToAxis: (axis) => {},
    focusOn: (target, opts) => {},
    setFOV: (v) => {},
    setState: (partial) => {},
    getState: () => {},
  },
  mode: {
    set: (mode, uuid) => {},
    get: () => {},
    canEnter: (uuid) => {},
    exit: () => {},
    focus: (uuid) => {},
  },
  micro: {
    enter: (uuid) => {},
    exit: () => {},
    isActive: () => {},
  },
  filters: {
    setTypeEnabled: (kind, enabled) => {},
    get: () => {},
  },
  runtime: {
    isFramePlaying: () => {},
    isCameraAuto: () => {},
  }
}

};

return hub; }

========================================== 📁 runtime/core/uiState.js

// runtime/core/uiState.js export function createUiState() { return { frame: { current: 0, range: { min: 0, max: 0 } }, selection: { kind: null, uuid: null }, cameraState: { theta: 0, phi: 0, distance: 10, target: {x:0,y:0,z:0}, fov: 50 }, mode: ‘macro’, filters: { points: true, lines: true, aux: true }, runtime: { isFramePlaying: false, isCameraAuto: false }, microState: null, viewerSettings: {}, visibleSet: new Set(), }; }

========================================== 📁 runtime/core/cameraEngine.js

// runtime/core/cameraEngine.js export class CameraEngine { constructor(initialState) { this.state = { …initialState }; }

rotate(dTheta, dPhi) {} pan(dx, dy) {} zoom(delta) {} reset() {} snapToAxis(axis) {} setState(partial) {} getState() { return this.state; } setFOV(value) {} }

========================================== 📁 runtime/core/frameController.js

// runtime/core/frameController.js export function createFrameController(uiState, visibilityController) { return { set: (n) => {}, get: () => {}, step: (delta) => {}, range: () => uiState.frame.range, startPlayback: () => {}, stopPlayback: () => {}, }; }

========================================== 📁 runtime/core/selectionController.js

// runtime/core/selectionController.js export function createSelectionController(uiState, structIndex) { return { select: (uuid) => {}, clear: () => {}, get: () => uiState.selection, }; }

========================================== 📁 runtime/core/modeController.js

// runtime/core/modeController.js export function createModeController(uiState, selectionController, microController, frameController, visibilityController, document) { return { set: (mode, uuid) => {}, get: () => uiState.mode, canEnter: (uuid) => false, exit: () => {}, focus: (uuid) => {}, }; }

========================================== 📁 runtime/core/microController.js

// runtime/core/microController.js export function createMicroController(structIndex) { return { compute: (selection, cameraState, document) => null }; }

========================================== 📁 runtime/core/visibilityController.js

// runtime/core/visibilityController.js export function createVisibilityController(uiState, document, structIndex) { return { recompute: () => new Set(), isVisible: (uuid) => false, getFilters: () => ({ …uiState.filters }), setTypeFilter: (kind, enabled) => {}, }; }

========================================== 📁 runtime/core/structIndex.js

// runtime/core/structIndex.js export function buildUUIDIndex(document) { return new Map(); }

export function detectFrameRange(document) { return { min: 0, max: 0 }; }

========================================== 📁 runtime/renderer/context.js

// runtime/renderer/context.js export function createRendererContext(canvas) { return { syncDocument: (doc, index) => {}, applyFrame: (visibleSet) => {}, updateCamera: (cameraState) => {}, applyMicroFX: (microState) => {}, applySelection: (selectionState) => {}, pickObjectAt: (ndcX, ndcY) => null, render: () => {}, }; }

========================================== 📁 ui/pointerInput.js

// ui/pointerInput.js export function createPointerInput(hub) { return { attach: (dom) => {}, detach: () => {}, }; }

========================================== 📁 ui/keyboardInput.js

// ui/keyboardInput.js export function createKeyboardInput(hub) { return { attach: () => {}, detach: () => {}, }; }

========================================== 📁 ui/gizmo.js

// ui/gizmo.js export function initGizmo(hub, overlayElement) {} export function updateGizmoOrientation(hub, overlayElement) {}

========================================== 📁 ui/picker.js

// ui/picker.js export function createPicker(hub) { return { attach: (dom) => {}, detach: () => {}, }; }

========================================== 📁 ui/timeline.js

// ui/timeline.js export function createTimeline(hub) { return { goTo: (n) => {}, play: () => {}, pause: () => {}, step: (d) => {}, }; }