mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 09:28:11 +00:00
artcraft: scaffold @storyteller/ui-pagescene library + PageSceneAdapter
Empty Nx library at frontend/libs/components/pagescene/ mirroring the @storyteller/ui-pagedraw shape (package.json with development export, tsconfig.lib.json with workspace lib references, vite.config.ts). Defines the platform-abstraction surface PageSceneAdapter: - Generation enqueue (model/provider/prompt/tokens/aspect/resolution) - Scene I/O (saveScene, loadScene, fetchAsset, host CDN/API getters) - Slot renderers for host-owned modals (renderAssetBrowser, renderSceneLoader) - Optional event hooks (onError, onSceneSaved, onSceneTitleChange, onEnqueueMeta, onSelectionChange, onSceneDirty) - Tab-cache integration (cacheJsonString + onSceneSerialized) so the artcraft useTabStore can roundtrip serialized scene JSON without the lib knowing about tabs Library code is empty for now -- file migrations (PageSceneStore + engine + comps + actions + hooks etc.) land in follow-up commits on this branch.
This commit is contained in:
1 parent
ca713ba1f9
commit
7465d2a533
7 files changed
+381
No files matched your search
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "@storyteller/ui-pagescene",
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index.d.ts",
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": {
|
||||
"development": "./src/index.ts",
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js",
|
||||
"default": "./dist/index.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
// Public API for @storyteller/ui-pagescene.
|
||||
// (Stub during scaffold; populated incrementally as files migrate.)
|
||||
|
||||
export {};
|
||||
@@ -0,0 +1,111 @@
|
||||
// Single platform-abstraction surface the host (artcraft Tauri app or
|
||||
// any future web host) injects into PageScene. Mirrors the shape of
|
||||
// PageDrawAdapter — methods for Tauri-specific generation/upload, slot
|
||||
// renderers for host-owned modals, and optional event hooks for
|
||||
// telemetry / error surfacing / tab persistence.
|
||||
//
|
||||
// The library itself has zero Tauri imports. Everything platform-
|
||||
// specific is on this interface; the adapter is constructed in the
|
||||
// host wrapper and passed to <PageScene adapter={adapter} />.
|
||||
|
||||
import type { ReactNode } from "react";
|
||||
import type {
|
||||
CommonAspectRatio,
|
||||
CommonResolution,
|
||||
ImageModel,
|
||||
} from "@storyteller/model-list";
|
||||
import type { GenerationProvider } from "@storyteller/api-enums";
|
||||
|
||||
// ─── Generation ────────────────────────────────────────────────────────
|
||||
|
||||
export interface PageSceneGenerateRequest {
|
||||
model?: ImageModel;
|
||||
provider?: GenerationProvider;
|
||||
prompt?: string;
|
||||
imageMediaTokens?: string[];
|
||||
sceneImageMediaToken?: string;
|
||||
imageCount?: number;
|
||||
aspectRatio?: CommonAspectRatio;
|
||||
resolution?: CommonResolution;
|
||||
frontendCaller?: string;
|
||||
frontendSubscriberId?: string;
|
||||
}
|
||||
|
||||
// ─── Scene I/O ─────────────────────────────────────────────────────────
|
||||
|
||||
export interface PageSceneSavePayload {
|
||||
saveJson: string;
|
||||
sceneTitle: string;
|
||||
sceneToken?: string;
|
||||
sceneThumbnail: Blob | undefined;
|
||||
}
|
||||
|
||||
// ─── Adapter ───────────────────────────────────────────────────────────
|
||||
|
||||
export interface PageSceneAdapter {
|
||||
// Generation enqueue. Same shape as PageDrawAdapter.enqueueEditImage.
|
||||
enqueueGeneration(
|
||||
req: PageSceneGenerateRequest,
|
||||
): Promise<{ status: string }>;
|
||||
|
||||
// Scene I/O — replaces the previous engine/api_manager.ts +
|
||||
// engine/api_fetchers.ts. The host owns all HTTP / FetchProxy
|
||||
// plumbing.
|
||||
saveScene(payload: PageSceneSavePayload): Promise<string>;
|
||||
loadScene(token: string): Promise<unknown>;
|
||||
// Wraps Tauri-flavored CORS-bypassed fetches. Used by Scene's GLTF
|
||||
// loader paths that resolve CDN URLs the browser can't fetch directly.
|
||||
fetchAsset(url: string): Promise<Response>;
|
||||
|
||||
// Hosts. Engine builds CDN URLs (`${cdnOrigin}${bucket_path}`) and
|
||||
// API URLs (`${apiSchemeAndHost}/v1/...`); the host supplies both.
|
||||
getCdnOrigin(): string;
|
||||
getApiSchemeAndHost(): string;
|
||||
getCurrentUserToken?(): string | undefined;
|
||||
|
||||
// Slot renders for host-owned UI. The library renders these inside
|
||||
// its own AssetMenu / scene-load modal containers — same shape as
|
||||
// PageDrawAdapter.renderBaseImageSelector.
|
||||
renderAssetBrowser(props: {
|
||||
onAssetSelect: (asset: {
|
||||
mediaToken: string;
|
||||
name: string;
|
||||
kind: string;
|
||||
}) => void;
|
||||
}): ReactNode;
|
||||
renderSceneLoader(props: {
|
||||
onSceneSelect: (token: string) => void;
|
||||
}): ReactNode;
|
||||
|
||||
// Optional event hooks — telemetry, host-side modals, tab title sync.
|
||||
onSelectionChange?(
|
||||
sel: { uuid: string; assetType: string } | null,
|
||||
): void;
|
||||
onSceneDirty?(dirty: boolean): void;
|
||||
onError?(err: { title: string; message: string }): void;
|
||||
onSceneSaved?(token: string): void;
|
||||
// Wraps the host's `signalScene(...)` so the artcraft TopBar (and
|
||||
// other app-wide consumers) keeps seeing scene title/owner/dirty
|
||||
// state without the lib importing the host signal.
|
||||
onSceneTitleChange?(meta: {
|
||||
title: string;
|
||||
token?: string;
|
||||
ownerToken?: string;
|
||||
isModified: boolean;
|
||||
}): void;
|
||||
onEnqueueMeta?(meta: {
|
||||
prompt: string;
|
||||
refImageUrls: string[];
|
||||
modelType: string;
|
||||
timestamp: number;
|
||||
}): void;
|
||||
|
||||
// Tab-cache integration. The host (artcraft useTabStore) reads/
|
||||
// writes the serialized scene JSON between tab switches; the library
|
||||
// is single-instance and tab-agnostic.
|
||||
cacheJsonString?: string;
|
||||
onSceneSerialized?(json: string): void;
|
||||
|
||||
// Initial scene to load on mount (the route param in artcraft).
|
||||
initialSceneToken?: string;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
{
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common"
|
||||
},
|
||||
{
|
||||
"path": "../../api"
|
||||
},
|
||||
{
|
||||
"path": "../../api-enums"
|
||||
},
|
||||
{
|
||||
"path": "../../model-list"
|
||||
},
|
||||
{
|
||||
"path": "../../soundboard"
|
||||
},
|
||||
{
|
||||
"path": "../promptbox"
|
||||
},
|
||||
{
|
||||
"path": "../popover"
|
||||
},
|
||||
{
|
||||
"path": "../loading"
|
||||
},
|
||||
{
|
||||
"path": "../toaster"
|
||||
},
|
||||
{
|
||||
"path": "../gallery-modal"
|
||||
},
|
||||
{
|
||||
"path": "../model-selector"
|
||||
},
|
||||
{
|
||||
"path": "../help-menu"
|
||||
},
|
||||
{
|
||||
"path": "../login-modal"
|
||||
},
|
||||
{
|
||||
"path": "../settings-modal"
|
||||
},
|
||||
{
|
||||
"path": "../modal"
|
||||
},
|
||||
{
|
||||
"path": "../tooltip"
|
||||
},
|
||||
{
|
||||
"path": "../button"
|
||||
},
|
||||
{
|
||||
"path": "../input"
|
||||
},
|
||||
{
|
||||
"path": "../slider-v2"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
],
|
||||
"extends": "../../../tsconfig.base.json"
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"lib": ["dom", "esnext"],
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client"
|
||||
],
|
||||
"rootDir": "src",
|
||||
"jsx": "react-jsx",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo"
|
||||
},
|
||||
"exclude": [
|
||||
"out-tsc",
|
||||
"dist",
|
||||
"**/*.spec.ts",
|
||||
"**/*.test.ts",
|
||||
"**/*.spec.tsx",
|
||||
"**/*.test.tsx",
|
||||
"**/*.spec.js",
|
||||
"**/*.test.js",
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx"
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../../common/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../api/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../api-enums/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../model-list/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../../soundboard/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../promptbox/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../popover/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../loading/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../toaster/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../gallery-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../model-selector/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../help-menu/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../login-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../settings-modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../modal/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../tooltip/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../button/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../input/tsconfig.lib.json"
|
||||
},
|
||||
{
|
||||
"path": "../slider-v2/tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest"
|
||||
],
|
||||
"jsx": "react-jsx"
|
||||
},
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts"
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.lib.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/// <reference types='vitest' />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import dts from 'vite-plugin-dts';
|
||||
import * as path from 'path';
|
||||
import { isExternal } from '../../shared-vite-config';
|
||||
|
||||
export default defineConfig(() => ({
|
||||
root: __dirname,
|
||||
cacheDir: '../../../node_modules/.vite/libs/components/pagescene',
|
||||
plugins: [react(), dts({ entryRoot: 'src', tsconfigPath: path.join(__dirname, 'tsconfig.lib.json') })],
|
||||
build: {
|
||||
outDir: './dist',
|
||||
emptyOutDir: true,
|
||||
reportCompressedSize: true,
|
||||
commonjsOptions: {
|
||||
transformMixedEsModules: true,
|
||||
},
|
||||
lib: {
|
||||
entry: 'src/index.ts',
|
||||
name: '@storyteller/ui-pagescene',
|
||||
fileName: 'index',
|
||||
formats: ['es' as const]
|
||||
},
|
||||
rollupOptions: {
|
||||
external: isExternal
|
||||
},
|
||||
},
|
||||
test: {
|
||||
watch: false,
|
||||
globals: true,
|
||||
environment: 'jsdom',
|
||||
include: ['{src,tests}/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
|
||||
reporters: ['default'],
|
||||
coverage: {
|
||||
reportsDirectory: './test-output/vitest/coverage',
|
||||
provider: 'v8' as const,
|
||||
}
|
||||
},
|
||||
}));
|
||||
Reference in new issue
Block a user