From c7d03c5d7f3e0db773c271ec4869a34768df878f Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:01:45 +0200 Subject: [PATCH 1/8] chore(rebrand): phase 1 repository metadata and package identity --- .mcp/proforge-mcp-server/package.json | 4 ++-- package.json | 14 ++++++++++++-- packages/collab-transport/package.json | 2 +- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.mcp/proforge-mcp-server/package.json b/.mcp/proforge-mcp-server/package.json index a461231f9..ef1ae023d 100644 --- a/.mcp/proforge-mcp-server/package.json +++ b/.mcp/proforge-mcp-server/package.json @@ -1,9 +1,9 @@ { - "name": "@storycraft/proforge-mcp-server", + "name": "@worldscript/proforge-mcp-server", "version": "1.0.0", "private": true, "type": "module", - "description": "MCP server exposing the StoryCraft ProForge Core Capability Layer to external AI agents", + "description": "MCP server exposing the WorldScript ProForge Core Capability Layer to external AI agents", "bin": { "proforge-mcp-server": "src/index.ts" }, diff --git a/package.json b/package.json index 5de9d178a..335714d9a 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,17 @@ { - "name": "storycraft-studio", + "name": "worldscript-studio", "private": true, - "version": "1.22.0", + "version": "1.23.0", + "description": "WorldScript Studio — offline-first, AI-powered creative writing application.", + "author": "QNBS", + "keywords": [ + "worldscript", + "worldscript-studio", + "creative-writing", + "ai-writing", + "pwa", + "tauri" + ], "type": "module", "packageManager": "pnpm@11.5.2", "engines": { diff --git a/packages/collab-transport/package.json b/packages/collab-transport/package.json index 9d51cf517..e511608a4 100644 --- a/packages/collab-transport/package.json +++ b/packages/collab-transport/package.json @@ -3,7 +3,7 @@ "version": "10.3.0-sc1", "private": true, "type": "module", - "description": "Vendored y-webrtc with StoryCraft RTCDataChannel E2E encryption patch", + "description": "Vendored y-webrtc with WorldScript RTCDataChannel E2E encryption patch", "main": "./src/index.ts", "types": "./src/index.ts", "exports": { From 94350367a2c483f8750eb828ebaa6b72a98c1536 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:09:59 +0200 Subject: [PATCH 2/8] chore(rebrand): phase 2 build tooling, urls and pwa configuration --- .lighthouserc.cjs | 2 +- .lighthouserc.desktop.cjs | 2 +- index.html | 20 ++++++++++---------- playwright.config.ts | 2 +- public/manifest.json | 30 +++++++++++++++--------------- public/sw.js | 24 ++++++++++++------------ src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- vite.config.ts | 8 ++++---- 9 files changed, 46 insertions(+), 46 deletions(-) diff --git a/.lighthouserc.cjs b/.lighthouserc.cjs index 275e9d384..6dc6f6311 100644 --- a/.lighthouserc.cjs +++ b/.lighthouserc.cjs @@ -3,7 +3,7 @@ module.exports = { collect: { startServerCommand: 'pnpm run preview', startServerReadyPattern: 'Local', - url: ['http://127.0.0.1:4173/StoryCraft-Studio/'], + url: ['http://127.0.0.1:4173/WorldScript-Studio/'], numberOfRuns: 3, settings: { // QNBS-v3: harden Chrome launch on CI runners. `--disable-dev-shm-usage` is the fix for the diff --git a/.lighthouserc.desktop.cjs b/.lighthouserc.desktop.cjs index 9d04c8277..771ca57f1 100644 --- a/.lighthouserc.desktop.cjs +++ b/.lighthouserc.desktop.cjs @@ -6,7 +6,7 @@ module.exports = { collect: { startServerCommand: 'pnpm run preview', startServerReadyPattern: 'Local', - url: ['http://127.0.0.1:4173/StoryCraft-Studio/'], + url: ['http://127.0.0.1:4173/WorldScript-Studio/'], numberOfRuns: 2, settings: { // QNBS-v3: same /dev/shm crash hardening as the mobile config (.lighthouserc.cjs) — diff --git a/index.html b/index.html index 29ff0c68b..bc534caa7 100644 --- a/index.html +++ b/index.html @@ -18,31 +18,31 @@ - + - - - - + + + + - - + + - + - + diff --git a/playwright.config.ts b/playwright.config.ts index 89d1ca401..3f15a9c7a 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -53,7 +53,7 @@ export default defineConfig({ }, use: { - baseURL: 'http://127.0.0.1:3000/StoryCraft-Studio', + baseURL: 'http://127.0.0.1:3000/WorldScript-Studio', trace: 'on-first-retry', screenshot: 'only-on-failure', // QNBS-v3: action + navigation timeouts bound to detect UI hangs / stalled diff --git a/public/manifest.json b/public/manifest.json index 7b1ff3949..d5d66a40d 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -1,10 +1,10 @@ { - "name": "StoryCraft Studio", - "short_name": "StoryCraft", - "description": "AI-Powered Creative Writing Studio — Craft entire novels, characters & worlds with Gemini AI.", - "start_url": "/StoryCraft-Studio/?source=pwa", - "scope": "/StoryCraft-Studio/", - "id": "/StoryCraft-Studio/", + "name": "WorldScript Studio", + "short_name": "WorldScript", + "description": "AI-Powered Creative Writing Studio — Build immersive worlds and craft entire novels with AI.", + "start_url": "/WorldScript-Studio/?source=pwa", + "scope": "/WorldScript-Studio/", + "id": "/WorldScript-Studio/", "display": "standalone", "display_override": ["window-controls-overlay", "standalone", "minimal-ui"], @@ -45,42 +45,42 @@ "name": "Templates", "short_name": "Templates", "description": "Browse narrative templates and start a structured project", - "url": "/StoryCraft-Studio/?view=templates&source=shortcut", + "url": "/WorldScript-Studio/?view=templates&source=shortcut", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] }, { "name": "My Manuscript", "short_name": "Manuscript", "description": "Open your manuscript view", - "url": "/StoryCraft-Studio/?view=manuscript&source=shortcut", + "url": "/WorldScript-Studio/?view=manuscript&source=shortcut", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] }, { "name": "Characters", "short_name": "Characters", "description": "Manage your characters", - "url": "/StoryCraft-Studio/?view=characters&source=shortcut", + "url": "/WorldScript-Studio/?view=characters&source=shortcut", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] }, { "name": "AI Writer", "short_name": "Writer", "description": "Open the AI writing studio", - "url": "/StoryCraft-Studio/?view=writer&source=shortcut", + "url": "/WorldScript-Studio/?view=writer&source=shortcut", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] }, { "name": "New Scene", "short_name": "New Scene", "description": "Jump to the Plot Board and add a new scene", - "url": "/StoryCraft-Studio/#/board?action=add-scene", + "url": "/WorldScript-Studio/#/board?action=add-scene", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] }, { "name": "Start Writing Session", "short_name": "Session", "description": "Open the Progress Tracker and start a writing session", - "url": "/StoryCraft-Studio/#/progress?action=start-session", + "url": "/WorldScript-Studio/#/progress?action=start-session", "icons": [{ "src": "favicon.svg", "sizes": "any", "type": "image/svg+xml" }] } ], @@ -92,7 +92,7 @@ "handle_links": "preferred", "share_target": { - "action": "/StoryCraft-Studio/", + "action": "/WorldScript-Studio/", "method": "GET", "params": { "title": "share_title", @@ -103,8 +103,8 @@ "protocol_handlers": [ { - "protocol": "web+storycraft", - "url": "/StoryCraft-Studio/?protocol=%s" + "protocol": "web+worldscript", + "url": "/WorldScript-Studio/?protocol=%s" } ], diff --git a/public/sw.js b/public/sw.js index 383f468b6..929ec1ca9 100644 --- a/public/sw.js +++ b/public/sw.js @@ -1,14 +1,14 @@ // ============================================================ -// StoryCraft Studio — Advanced Service Worker v3.0 +// WorldScript Studio — Advanced Service Worker v3.0 // Strategy: Stale-While-Revalidate + Cache-First + Network-Only // Features: Versioned caches, background sync, periodic updates, // offline fallback, push notifications, share target // ============================================================ -const APP_VERSION = '1.22.0'; -const CACHE_STATIC = `storycraft-static-v${APP_VERSION}`; -const CACHE_DYNAMIC = `storycraft-dynamic-v${APP_VERSION}`; -const CACHE_IMAGES = `storycraft-images-v${APP_VERSION}`; +const APP_VERSION = '1.23.0'; +const CACHE_STATIC = `worldscript-static-v${APP_VERSION}`; +const CACHE_DYNAMIC = `worldscript-dynamic-v${APP_VERSION}`; +const CACHE_IMAGES = `worldscript-images-v${APP_VERSION}`; const ALL_CACHES = [CACHE_STATIC, CACHE_DYNAMIC, CACHE_IMAGES]; const BASE = self.location.pathname.replace(/sw\.js$/, ''); @@ -73,7 +73,7 @@ async function trimCache(cacheName, maxEntries) { async function offlineFallback(request) { if (request.destination === 'document') { const cached = await caches.match(`${BASE}offline.html`); - return cached || new Response('Offline

StoryCraft Studio ist offline.

', { + return cached || new Response('Offline

WorldScript Studio ist offline.

', { headers: { 'Content-Type': 'text/html' }, status: 503, }); @@ -305,13 +305,13 @@ self.addEventListener('message', (event) => { self.addEventListener('push', (event) => { if (!event.data) return; let data; - try { data = event.data.json(); } catch { data = { title: 'StoryCraft Studio', body: event.data.text() }; } + try { data = event.data.json(); } catch { data = { title: 'WorldScript Studio', body: event.data.text() }; } const options = { body: data.body || 'New notification', icon: `${BASE}favicon.svg`, badge: `${BASE}favicon.svg`, - tag: data.tag || 'storycraft-notification', + tag: data.tag || 'worldscript-notification', data: data.data || {}, actions: data.actions || [], vibrate: [100, 50, 100], @@ -320,7 +320,7 @@ self.addEventListener('push', (event) => { }; event.waitUntil( - self.registration.showNotification(data.title || 'StoryCraft Studio', options) + self.registration.showNotification(data.title || 'WorldScript Studio', options) ); }); @@ -342,7 +342,7 @@ self.addEventListener('notificationclick', (event) => { // BACKGROUND SYNC — Retry deferred autosave operations // ════════════════════════════════════════════════════════════ self.addEventListener('sync', (event) => { - if (event.tag === 'storycraft-autosave') { + if (event.tag === 'worldscript-autosave') { event.waitUntil( self.clients.matchAll().then((clients) => clients.forEach((c) => c.postMessage({ type: 'TRIGGER_AUTOSAVE' })) @@ -355,7 +355,7 @@ self.addEventListener('sync', (event) => { // PERIODIC BACKGROUND SYNC — Refresh locale JSON while idle // ════════════════════════════════════════════════════════════ self.addEventListener('periodicsync', (event) => { - if (event.tag === 'storycraft-refresh') { + if (event.tag === 'worldscript-refresh') { event.waitUntil( caches.open(CACHE_DYNAMIC).then(async (cache) => { const keys = await cache.keys(); @@ -371,4 +371,4 @@ self.addEventListener('periodicsync', (event) => { } }); -swLogger.log(`StoryCraft Studio Service Worker v${APP_VERSION} loaded`); +swLogger.log(`WorldScript Studio Service Worker v${APP_VERSION} loaded`); diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dfc8e0cae..6d934f754 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "storycraft-studio" -version = "1.22.0" +version = "1.23.0" description = "AI-powered creative writing application" authors = ["QNBS"] license = "MIT" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 02addd97e..76d8fea92 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", "productName": "StoryCraft Studio", - "version": "1.22.0", + "version": "1.23.0", "identifier": "com.storycraft.studio", "build": { "frontendDist": "../dist", diff --git a/vite.config.ts b/vite.config.ts index 28f28bff6..002e4c38f 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -21,7 +21,7 @@ function resolveBase(): string { if (process.env['DEPLOY_TARGET'] === 'edge') { return '/'; } - return '/StoryCraft-Studio/'; + return '/WorldScript-Studio/'; } const deployBase = resolveBase(); @@ -49,10 +49,10 @@ export default defineConfig({ tailwindcss(), react(), { - name: 'storycraft-deploy-base-html', + name: 'worldscript-deploy-base-html', transformIndexHtml(html) { - if (deployBase === '/StoryCraft-Studio/') return html; - return html.replaceAll('/StoryCraft-Studio/', deployBase); + if (deployBase === '/WorldScript-Studio/') return html; + return html.replaceAll('/WorldScript-Studio/', deployBase); }, }, VitePWA({ From e3fbc604cdb57a32f1dff1751abed06f3a63e73c Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:19:26 +0200 Subject: [PATCH 3/8] chore(rebrand): phase 3 tauri desktop identity --- src-tauri/Cargo.lock | 48 ++++++++++++++++++------------------ src-tauri/Cargo.toml | 6 ++--- src-tauri/Entitlements.plist | 2 +- src-tauri/tauri.conf.json | 32 ++++++++++++------------ 4 files changed, 44 insertions(+), 44 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3fdf4bbef..20d8f13b4 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4634,30 +4634,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" -[[package]] -name = "storycraft-studio" -version = "1.21.0" -dependencies = [ - "base64 0.22.1", - "candle-core", - "candle-nn", - "log", - "serde", - "serde_json", - "tauri", - "tauri-build", - "tauri-plugin-deep-link", - "tauri-plugin-dialog", - "tauri-plugin-fs", - "tauri-plugin-http", - "tauri-plugin-log", - "tauri-plugin-shell", - "tauri-plugin-single-instance", - "tauri-plugin-updater", - "tauri-plugin-window-state", - "tokio", -] - [[package]] name = "string_cache" version = "0.9.0" @@ -6771,6 +6747,30 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "worldscript-studio" +version = "1.23.0" +dependencies = [ + "base64 0.22.1", + "candle-core", + "candle-nn", + "log", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-deep-link", + "tauri-plugin-dialog", + "tauri-plugin-fs", + "tauri-plugin-http", + "tauri-plugin-log", + "tauri-plugin-shell", + "tauri-plugin-single-instance", + "tauri-plugin-updater", + "tauri-plugin-window-state", + "tokio", +] + [[package]] name = "writeable" version = "0.6.3" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6d934f754..d4da237e0 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "storycraft-studio" +name = "worldscript-studio" version = "1.23.0" -description = "AI-powered creative writing application" +description = "AI-powered creative writing and world-building application" authors = ["QNBS"] license = "MIT" -repository = "https://github.com/QNBS/StoryCraft-Studio" +repository = "https://github.com/QNBS/WorldScript-Studio" edition = "2021" rust-version = "1.77.2" diff --git a/src-tauri/Entitlements.plist b/src-tauri/Entitlements.plist index 94f83fd90..2d2aa679f 100644 --- a/src-tauri/Entitlements.plist +++ b/src-tauri/Entitlements.plist @@ -2,7 +2,7 @@ - + diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 76d8fea92..dd70ee74a 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,8 +1,8 @@ { "$schema": "../node_modules/@tauri-apps/cli/config.schema.json", - "productName": "StoryCraft Studio", + "productName": "WorldScript Studio", "version": "1.23.0", - "identifier": "com.storycraft.studio", + "identifier": "com.worldscript.studio", "build": { "frontendDist": "../dist", "devUrl": "http://localhost:3000", @@ -12,7 +12,7 @@ "app": { "windows": [ { - "title": "StoryCraft Studio", + "title": "WorldScript Studio", "width": 1280, "height": 800, "minWidth": 800, @@ -34,8 +34,8 @@ "category": "Productivity", "publisher": "QNBS", "copyright": "© 2026 QNBS. Licensed under MIT.", - "shortDescription": "AI-powered creative writing application", - "longDescription": "StoryCraft Studio is an offline-first, AI-powered creative writing application with local LLM inference, plot boards, character management, and manuscript export.", + "shortDescription": "AI-powered creative writing and world-building application", + "longDescription": "WorldScript Studio is an offline-first, AI-powered creative writing application with local LLM inference, plot boards, character management, and manuscript export.", "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -45,17 +45,17 @@ ], "fileAssociations": [ { - "ext": ["storycraft"], - "name": "StoryCraft Project", - "description": "StoryCraft Studio creative writing project", - "mimeType": "application/x-storycraft-project", + "ext": ["worldscript"], + "name": "WorldScript Project", + "description": "WorldScript Studio creative writing project", + "mimeType": "application/x-worldscript-project", "role": "Editor" }, { - "ext": ["scst"], - "name": "StoryCraft Project", - "description": "StoryCraft Studio creative writing project (short extension)", - "mimeType": "application/x-storycraft-project", + "ext": ["wsst"], + "name": "WorldScript Project", + "description": "WorldScript Studio creative writing project (short extension)", + "mimeType": "application/x-worldscript-project", "role": "Editor" } ], @@ -74,8 +74,8 @@ "updater": { "active": true, "endpoints": [ - "https://github.com/qnbs/StoryCraft-Studio/releases/latest/download/latest.json", - "https://qnbs.github.io/StoryCraft-Studio/updater/latest.json" + "https://github.com/qnbs/WorldScript-Studio/releases/latest/download/latest.json", + "https://qnbs.github.io/WorldScript-Studio/updater/latest.json" ], "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDZBOTlDOEIxNUYyQzA5RUEKUldUcUNTeGZzY2laYW8yU21QNldMbzZQcTFmUDNTdVZ5RHhNenFkSTVGNTRoTllTRXpBcjJyVVMK", "windows": { @@ -84,7 +84,7 @@ }, "deep-link": { "desktop": { - "schemes": ["storycraft"] + "schemes": ["worldscript"] } } } From 8672c9a26afb8a1c83284ecab552e2d7b0c19f99 Mon Sep 17 00:00:00 2001 From: qnbs <155236708+qnbs@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:33:52 +0200 Subject: [PATCH 4/8] chore(rebrand): phase 4 source strings, identifiers and file renames --- .mcp/proforge-mcp-server/src/index.ts | 2 +- .../proforge-mcp-server/src/tools/runStage.ts | 2 +- .mcp/proforge-mcp-server/src/tools/shared.ts | 2 +- App.tsx | 17 ++-- app/listenerMiddleware.ts | 6 +- components/CollaborationPanel.tsx | 2 +- components/CommandPalette.tsx | 2 +- components/Dashboard.tsx | 2 +- components/TemplateView.tsx | 2 +- components/WelcomePortal.tsx | 2 +- .../dashboard/BackupQuickActionsCard.tsx | 2 +- components/lora/LoraDatasetBuilder.tsx | 2 +- components/scene-board/PlotCanvas.tsx | 2 +- components/settings/CommunitySection.tsx | 4 +- components/settings/DataSection.tsx | 4 +- components/settings/GeneralSections.tsx | 6 +- components/settings/IdbUnlockModal.tsx | 4 +- components/settings/LocalAiSection.tsx | 2 +- components/ui/ErrorBoundary.tsx | 2 +- contexts/I18nContext.tsx | 2 +- features/featureCatalog.ts | 2 +- features/featureFlags/featureFlagsSlice.ts | 16 +++- features/lora/loraSlice.ts | 4 +- features/lora/loraThunks.ts | 2 +- features/mindMap/mindMapUiSlice.ts | 2 +- features/plotBoard/plotBoardSlice.ts | 2 +- features/proForge/types.ts | 2 +- .../progressTracker/progressTrackerSlice.ts | 2 +- features/sceneComments/sceneCommentsSlice.ts | 2 +- features/settings/settingsSlice.ts | 10 +- hooks/useApp.ts | 10 +- hooks/useGlobalCopilot.ts | 8 +- hooks/usePWA.ts | 10 +- hooks/useSettingsView.ts | 6 +- ...useStoryCraftAI.ts => useWorldScriptAI.ts} | 18 ++-- hooks/useWriterView.ts | 4 +- index.html | 4 +- index.tsx | 4 +- packages/ai-core/src/index.ts | 4 +- packages/ai-core/src/tabLeaderElection.ts | 4 +- packages/collab-transport/src/crypto.js | 4 +- packages/collab-transport/src/index.ts | 2 +- packages/ui/src/tokens.ts | 2 +- packages/ui/tailwind-preset.ts | 2 +- packages/worker-bus/src/deadLetterQueue.ts | 2 +- plugins/example-word-counter/index.ts | 2 +- register-sw.ts | 26 ++--- scripts/audit-feature-parity.ts | 2 +- scripts/cf-pages-deploy.mjs | 2 +- scripts/check-i18n-keys.mjs | 2 +- scripts/resolve-deploy-base.mjs | 4 +- scripts/smoke-prod-build.mjs | 2 +- scripts/sync-deploy-base.mjs | 4 +- services/ai/aiInferenceCacheService.ts | 4 +- services/ai/benchmarkService.ts | 2 +- services/ai/ecoModeService.ts | 2 +- services/ai/fetchAdapter.ts | 6 +- services/ai/index.ts | 13 ++- services/ai/inferenceProgressEmitter.ts | 2 +- services/ai/openrouterModels.ts | 2 +- services/ai/providerFactory.ts | 12 +-- services/ai/providers/openrouterProvider.ts | 6 +- services/ai/telemetryService.ts | 2 +- ...Fetch.ts => worldScriptCompletionFetch.ts} | 24 ++--- services/aiProviderService.ts | 4 +- services/cloudSync/cloudSyncEncryption.ts | 2 +- services/collaborationService.ts | 2 +- services/commands/palettePreferences.ts | 2 +- services/copilot/copilotContextService.ts | 4 +- services/dbConstants.ts | 8 +- services/dbInitialization.ts | 6 +- services/dbMigration.ts | 6 +- services/epubApiService.ts | 2 +- services/factoryResetService.ts | 14 +-- services/fs/settingsFsStore.ts | 4 +- services/geminiService.ts | 2 +- services/libraryBackupService.ts | 4 +- services/localAiFacade.ts | 2 +- services/logger.ts | 6 +- services/lora/loraOllamaService.ts | 4 +- services/lora/loraTrainingService.ts | 2 +- services/loraAdapterService.ts | 2 +- services/pluginRegistry.ts | 2 +- .../proForge/adapters/nodeInferenceGateway.ts | 4 +- services/projectImportSchema.ts | 2 +- services/promptLibrary.ts | 14 +-- services/sceneRevisionService.ts | 2 +- services/settingsExchange.ts | 4 +- services/spotlightTour.ts | 4 +- services/storage/idbCore.ts | 6 +- services/storage/idbKeyStore.ts | 2 +- services/storage/idbProjectStore.ts | 2 +- services/storage/storageEncryptionService.ts | 2 +- services/tauriDeepLink.ts | 26 ++--- services/tauriTaskBridge.ts | 6 +- services/voice/voiceCommandService.ts | 2 +- services/voice/wakeWordEngine.ts | 2 +- src-tauri/src/commands/task_supervisor.rs | 12 +-- src-tauri/src/lib.rs | 4 +- src-tauri/src/pandoc.rs | 2 +- stories/Drawer.stories.tsx | 2 +- stories/Modal.stories.tsx | 2 +- stories/PWAComponents.stories.tsx | 2 +- tests/e2e/helpers.ts | 6 +- tests/e2e/lora-wizard.spec.ts | 2 +- tests/unit/I18nContext.test.tsx | 32 +++---- tests/unit/ai/aiSmallModules.test.ts | 16 ++-- .../unit/ai/localModelStorageService.test.ts | 6 +- tests/unit/ai/openrouterModels.test.ts | 2 +- ....ts => worldScriptCompletionFetch.test.ts} | 94 +++++++++---------- tests/unit/aiCoreFallbackPaths.test.ts | 2 +- .../unit/commands/palettePreferences.test.ts | 2 +- tests/unit/copilot/useGlobalCopilot.test.ts | 6 +- tests/unit/dbConstants.test.ts | 6 +- tests/unit/dbInitialization.test.ts | 14 +-- tests/unit/dbMigration.test.ts | 14 +-- tests/unit/factoryResetService.test.ts | 6 +- ...aftAI.test.ts => useWorldScriptAI.test.ts} | 28 +++--- tests/unit/languageToolClient.test.ts | 4 +- tests/unit/logger.test.ts | 12 +-- tests/unit/lora/loraOllamaService.test.ts | 4 +- tests/unit/orchestrationAndFetch.test.ts | 8 +- tests/unit/palettePreferences.test.ts | 8 +- tests/unit/providerFactory.test.ts | 24 ++--- .../unit/services/ai/telemetryService.test.ts | 10 +- tests/unit/services/fetchAdapter.test.ts | 32 +++---- tests/unit/services/helpSearch.test.ts | 2 +- .../unit/services/palettePreferences.test.ts | 10 +- tests/unit/services/tauriDeepLink.test.ts | 28 +++--- tests/unit/settings/AiProviderCard.test.tsx | 2 +- tests/unit/settings/GeneralSections.test.tsx | 2 +- tests/unit/settings/WebLlmPanel.test.tsx | 2 +- tests/unit/settingsExchange.test.ts | 14 +-- tests/unit/settingsSlice.test.ts | 12 +-- .../storage/storageEncryptionService.test.ts | 6 +- tests/unit/storageEncryptionService.test.ts | 4 +- tests/unit/tabLeaderElection.test.ts | 2 +- tests/unit/tauriTaskBridge.test.ts | 2 +- tests/unit/useApp.test.ts | 6 +- tests/unit/useConsistencyCheckerView.test.tsx | 2 +- tests/unit/useCriticView.test.tsx | 2 +- tests/unit/usePWA.test.ts | 4 +- tests/unit/useWriterView.test.tsx | 6 +- tests/unit/voice/wakeWordEngine.test.ts | 24 ++--- ...yCraftAi.test.ts => worldScriptAi.test.ts} | 8 +- types.ts | 4 +- workers/duckdbWorker.ts | 6 +- workers/plugin.worker.ts | 2 +- workers/v2/duckdb.worker.ts | 6 +- 149 files changed, 504 insertions(+), 486 deletions(-) rename hooks/{useStoryCraftAI.ts => useWorldScriptAI.ts} (91%) rename services/ai/{storyCraftCompletionFetch.ts => worldScriptCompletionFetch.ts} (89%) rename tests/unit/ai/{storyCraftCompletionFetch.test.ts => worldScriptCompletionFetch.test.ts} (73%) rename tests/unit/hooks/{useStoryCraftAI.test.ts => useWorldScriptAI.test.ts} (81%) rename tests/unit/{storyCraftAi.test.ts => worldScriptAi.test.ts} (90%) diff --git a/.mcp/proforge-mcp-server/src/index.ts b/.mcp/proforge-mcp-server/src/index.ts index c9733fdd4..931b1c621 100644 --- a/.mcp/proforge-mcp-server/src/index.ts +++ b/.mcp/proforge-mcp-server/src/index.ts @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * ProForge MCP Server — exposes the StoryCraft ProForge Core Capability Layer to external AI agents. + * ProForge MCP Server — exposes the WorldScript ProForge Core Capability Layer to external AI agents. * QNBS-v3: A thin stdio MCP adapter. All business logic lives in the repo's Core Capability Layer * (services/proForge/proForgeCapabilityLayer.ts) via the Node adapter — this file only registers the * 5 tools and connects the transport. diff --git a/.mcp/proforge-mcp-server/src/tools/runStage.ts b/.mcp/proforge-mcp-server/src/tools/runStage.ts index e79a77c8d..3a7be5c82 100644 --- a/.mcp/proforge-mcp-server/src/tools/runStage.ts +++ b/.mcp/proforge-mcp-server/src/tools/runStage.ts @@ -17,7 +17,7 @@ export function registerRunStage(server: McpServer): void { 'Execute a single ProForge pipeline stage agent against the manuscript and return its ' + 'review items, metrics, raw output, and the heuristic supervisor decision. Stages: ' + 'intake, structural, lineProse, copyEdit, proof, production, publishing, analytics. ' + - 'Requires GEMINI_API_KEY (or STORYCRAFT_API_KEY) in the server environment.', + 'Requires GEMINI_API_KEY (or WORLDSCRIPT_API_KEY) in the server environment.', inputSchema: { project: projectShape.optional(), stage: z.enum([ diff --git a/.mcp/proforge-mcp-server/src/tools/shared.ts b/.mcp/proforge-mcp-server/src/tools/shared.ts index 5b515173e..bb1cd679d 100644 --- a/.mcp/proforge-mcp-server/src/tools/shared.ts +++ b/.mcp/proforge-mcp-server/src/tools/shared.ts @@ -31,7 +31,7 @@ export const projectShape = z .optional(), config: z.unknown().optional(), }) - .describe("Portable StoryCraft project payload. Omit to use the server's --project file."); + .describe("Portable WorldScript project payload. Omit to use the server's --project file."); export interface ToolResult { content: Array<{ type: 'text'; text: string }>; diff --git a/App.tsx b/App.tsx index 2ff7b0cf3..f3782ad15 100644 --- a/App.tsx +++ b/App.tsx @@ -219,7 +219,7 @@ const App: FC = ({ isNewUser }) => { themeColorMeta.setAttribute('content', themeColor); } try { - localStorage.setItem('storycraft-theme', isDark ? 'dark' : 'light'); + localStorage.setItem('worldscript-theme', isDark ? 'dark' : 'light'); } catch { // localStorage may be unavailable (SSR, quota exceeded) } @@ -257,7 +257,7 @@ const App: FC = ({ isNewUser }) => { useEffect(() => { document.body.classList.toggle( - 'storycraft-reduced-motion', + 'worldscript-reduced-motion', settings.accessibility.reducedMotion, ); }, [settings.accessibility.reducedMotion]); @@ -265,12 +265,15 @@ const App: FC = ({ isNewUser }) => { // QNBS-v3: Barrierefreiheits-Toggles → dokumentweite Klassen (Tokens in index.css). useEffect(() => { document.documentElement.classList.toggle( - 'storycraft-large-text', + 'worldscript-large-text', settings.accessibility.largeText, ); - document.body.classList.toggle('storycraft-screen-reader', settings.accessibility.screenReader); document.body.classList.toggle( - 'storycraft-focus-indicators', + 'worldscript-screen-reader', + settings.accessibility.screenReader, + ); + document.body.classList.toggle( + 'worldscript-focus-indicators', settings.accessibility.focusIndicators, ); document.body.classList.toggle( @@ -351,7 +354,7 @@ const App: FC = ({ isNewUser }) => { const combined = [shareTitle, shareText, shareUrl].filter(Boolean).join('\n'); const preview = combined.length > 280 ? `${combined.slice(0, 277).trimEnd()}…` : combined; try { - sessionStorage.setItem('storycraft-share-target-payload', combined); + sessionStorage.setItem('worldscript-share-target-payload', combined); } catch { /* quota / privacy mode */ } @@ -514,7 +517,7 @@ const App: FC = ({ isNewUser }) => { return () => unregisterTauriMenuHandler(); }, [executeCommand]); - // QNBS-v3: Tauri deep link handler for native file associations (.storycraft, .scst) + // QNBS-v3: Tauri deep link handler for native file associations (.worldscript, .wsst) useEffect(() => { let cleanup: (() => void) | undefined; void initTauriDeepLink(dispatch, t).then((fn) => { diff --git a/app/listenerMiddleware.ts b/app/listenerMiddleware.ts index d0d3fd3e5..245a1d857 100644 --- a/app/listenerMiddleware.ts +++ b/app/listenerMiddleware.ts @@ -378,7 +378,7 @@ listenerMiddleware.startListening({ effect: async () => { // QNBS-v3: guard for SSR / worker contexts where window is not defined if (typeof window !== 'undefined') { - window.__storycraft_adaptive_ai__ = true; + window.__worldscript_adaptive_ai__ = true; } try { const { generateDeviceProfile } = await import('../services/ai/localAiDeviceProfiler'); @@ -399,7 +399,7 @@ listenerMiddleware.startListening({ effect: async () => { // QNBS-v3: guard for SSR / worker contexts where window is not defined if (typeof window !== 'undefined') { - window.__storycraft_adaptive_ai__ = false; + window.__worldscript_adaptive_ai__ = false; } try { const [aiCore, profiler] = await Promise.all([ @@ -426,7 +426,7 @@ listenerMiddleware.startListening({ export function initAdaptiveAiOnStartup(enabled: boolean): void { if (!enabled) return; if (typeof window !== 'undefined') { - window.__storycraft_adaptive_ai__ = true; + window.__worldscript_adaptive_ai__ = true; } // Profile generation is handled by useAdaptiveAi on first mount logger.info('Adaptive AI engine: window gate set on cold start'); diff --git a/components/CollaborationPanel.tsx b/components/CollaborationPanel.tsx index 524ef9043..6f1bcfc3a 100644 --- a/components/CollaborationPanel.tsx +++ b/components/CollaborationPanel.tsx @@ -222,7 +222,7 @@ export const CollaborationPanel: FC = ({ isOpen, onClos setConnectedUsers([]); }, []); - const currentRoomId = collaborationService.roomId ?? `storycraft-${projectId}`; + const currentRoomId = collaborationService.roomId ?? `worldscript-${projectId}`; if (!isOpen) return null; diff --git a/components/CommandPalette.tsx b/components/CommandPalette.tsx index 7b21077e5..17d38ac8b 100644 --- a/components/CommandPalette.tsx +++ b/components/CommandPalette.tsx @@ -588,7 +588,7 @@ export const CommandPalette: React.FC = ({ {t('palette.footer.pinHint')} - StoryCraft Studio + WorldScript Studio diff --git a/components/Dashboard.tsx b/components/Dashboard.tsx index 488d645f9..4fff4f6ea 100644 --- a/components/Dashboard.tsx +++ b/components/Dashboard.tsx @@ -490,7 +490,7 @@ const DashboardModals: FC = () => { ); }; -const DASHBOARD_ONBOARDING_KEY = 'storycraft-dashboard-onboarding-dismissed'; +const DASHBOARD_ONBOARDING_KEY = 'worldscript-dashboard-onboarding-dismissed'; const OnboardingTipsBanner: FC = () => { const { t } = useTranslation(); diff --git a/components/TemplateView.tsx b/components/TemplateView.tsx index b28b41a24..d66253de0 100644 --- a/components/TemplateView.tsx +++ b/components/TemplateView.tsx @@ -455,7 +455,7 @@ const CommunityTab: FC = () => { title: s.title, content: s.description ? `# ${s.title}\n\n${s.description}` : '', })); - const event = new CustomEvent('storycraft:applyTemplate', { + const event = new CustomEvent('worldscript:applyTemplate', { detail: { title: ct.name, sections }, }); window.dispatchEvent(event); diff --git a/components/WelcomePortal.tsx b/components/WelcomePortal.tsx index 3972ee767..3210f5eb7 100644 --- a/components/WelcomePortal.tsx +++ b/components/WelcomePortal.tsx @@ -170,7 +170,7 @@ export const WelcomePortal: React.FC = ({ onExit }) => { projectGoals: { totalWordCount: 15000, targetDate: null }, writingHistory: [] as const, }; - return new File([JSON.stringify(payload)], 'storycraft-demo.json', { + return new File([JSON.stringify(payload)], 'worldscript-demo.json', { type: 'application/json', }); }; diff --git a/components/dashboard/BackupQuickActionsCard.tsx b/components/dashboard/BackupQuickActionsCard.tsx index 154a35394..41a121d2c 100644 --- a/components/dashboard/BackupQuickActionsCard.tsx +++ b/components/dashboard/BackupQuickActionsCard.tsx @@ -10,7 +10,7 @@ import type { ProjectSnapshot, StoryProject, View } from '../../types'; import { Button } from '../ui/Button'; import { Card, CardContent, CardHeader } from '../ui/Card'; -export const SETTINGS_CATEGORY_STORAGE_KEY = 'storycraft-settings-category'; +export const SETTINGS_CATEGORY_STORAGE_KEY = 'worldscript-settings-category'; export interface BackupQuickActionsCardProps { onNavigate: (view: View) => void; diff --git a/components/lora/LoraDatasetBuilder.tsx b/components/lora/LoraDatasetBuilder.tsx index 8ef617aa2..33637b4db 100644 --- a/components/lora/LoraDatasetBuilder.tsx +++ b/components/lora/LoraDatasetBuilder.tsx @@ -66,7 +66,7 @@ export default React.memo(function LoraDatasetBuilder() { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = `storycraft-dataset-${format}-${Date.now()}.jsonl`; + a.download = `worldscript-dataset-${format}-${Date.now()}.jsonl`; a.click(); URL.revokeObjectURL(url); }, diff --git a/components/scene-board/PlotCanvas.tsx b/components/scene-board/PlotCanvas.tsx index 08daa7c3d..e57751382 100644 --- a/components/scene-board/PlotCanvas.tsx +++ b/components/scene-board/PlotCanvas.tsx @@ -165,7 +165,7 @@ export const PlotCanvas: FC = ({ const rafRef = useRef(null); const reducedMotion = typeof document !== 'undefined' && - document.body.classList.contains('storycraft-reduced-motion'); + document.body.classList.contains('worldscript-reduced-motion'); // Cancel any pending rAF on unmount to prevent dispatching into an unmounted tree. useEffect( diff --git a/components/settings/CommunitySection.tsx b/components/settings/CommunitySection.tsx index 8520dc628..3a23c0f39 100644 --- a/components/settings/CommunitySection.tsx +++ b/components/settings/CommunitySection.tsx @@ -30,7 +30,7 @@ export const CommunitySection: React.FC = () => {
{ { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = `storycraft-library-${new Date().toISOString().slice(0, 10)}.zip`; + a.download = `worldscript-library-${new Date().toISOString().slice(0, 10)}.zip`; a.click(); URL.revokeObjectURL(url); dispatch( @@ -75,7 +75,7 @@ export const DataSection: FC = () => { const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; - a.download = 'storycraft-settings.json'; + a.download = 'worldscript-settings.json'; a.click(); URL.revokeObjectURL(url); }; diff --git a/components/settings/GeneralSections.tsx b/components/settings/GeneralSections.tsx index e2c09ceb2..e0584b2e5 100644 --- a/components/settings/GeneralSections.tsx +++ b/components/settings/GeneralSections.tsx @@ -421,7 +421,7 @@ export const AboutSection: FC = React.memo(() => { > {ICONS.WRITER} -

StoryCraft Studio

+

WorldScript Studio

{t('settings.about.versionLabel')} {packageJson.version}

@@ -429,7 +429,7 @@ export const AboutSection: FC = React.memo(() => {

{t('settings.about.description')}

{ {t('settings.about.githubLabel')} void; } -const ATTEMPT_STORAGE_KEY = 'storycraft-idb-unlock-attempts'; -const LOCKOUT_STORAGE_KEY = 'storycraft-idb-unlock-lockout'; +const ATTEMPT_STORAGE_KEY = 'worldscript-idb-unlock-attempts'; +const LOCKOUT_STORAGE_KEY = 'worldscript-idb-unlock-lockout'; function getAttemptCount(): number { try { diff --git a/components/settings/LocalAiSection.tsx b/components/settings/LocalAiSection.tsx index f6c26765b..f1102f111 100644 --- a/components/settings/LocalAiSection.tsx +++ b/components/settings/LocalAiSection.tsx @@ -434,7 +434,7 @@ export const LocalAiSection: FC = () => { : t('settings.ai.localAi.perfNoData')}

void }) { variant="ghost" onClick={() => window.open( - 'https://github.com/qnbs/StoryCraft-Studio/issues/new', + 'https://github.com/qnbs/WorldScript-Studio/issues/new', '_blank', 'noopener,noreferrer', ) diff --git a/contexts/I18nContext.tsx b/contexts/I18nContext.tsx index a3a92258f..e96311c3c 100644 --- a/contexts/I18nContext.tsx +++ b/contexts/I18nContext.tsx @@ -202,7 +202,7 @@ interface I18nProviderProps { children: ReactNode; } -const LANG_KEY = 'storycraft-language'; +const LANG_KEY = 'worldscript-language'; const VALID_LANGS: Language[] = ['en', 'de', 'fr', 'es', 'it', 'ar', 'he', 'ja', 'zh', 'pt', 'el']; const getInitialLanguage = (): Language => { diff --git a/features/featureCatalog.ts b/features/featureCatalog.ts index a07361aac..17b0d013b 100644 --- a/features/featureCatalog.ts +++ b/features/featureCatalog.ts @@ -307,7 +307,7 @@ export const FEATURE_CATALOG: FeatureCatalogEntry[] = [ }, { file: 'hooks/useLoraView.ts:50', description: 'Returns isEnabled=false when off' }, { - file: 'hooks/useStoryCraftAI.ts:39', + file: 'hooks/useWorldScriptAI.ts:39', description: 'Reads selectActiveLoraOllamaTag; passes ollamaModelTag as loraModelPath in AIRequestOptions — C-3 wiring complete (df185c7)', }, diff --git a/features/featureFlags/featureFlagsSlice.ts b/features/featureFlags/featureFlagsSlice.ts index a7fd3a2f8..e4a054d59 100644 --- a/features/featureFlags/featureFlagsSlice.ts +++ b/features/featureFlags/featureFlagsSlice.ts @@ -51,7 +51,8 @@ export interface FeatureFlagsState { enableGlobalCopilot: boolean; } -const FEATURE_FLAGS_STORAGE_KEY = 'storycraft-feature-flags'; +const FEATURE_FLAGS_STORAGE_KEY = 'worldscript-feature-flags'; +const LEGACY_FEATURE_FLAGS_STORAGE_KEY = 'worldscript-feature-flags'; const defaultFeatureFlagsState: FeatureFlagsState = { // QNBS-v3: all flags on by default so new installs get the full feature set immediately @@ -89,7 +90,18 @@ const loadFeatureFlagsState = (): FeatureFlagsState => { } try { - const stored = localStorage.getItem(FEATURE_FLAGS_STORAGE_KEY); + let stored = localStorage.getItem(FEATURE_FLAGS_STORAGE_KEY); + + // QNBS-v3: Rebrand migration — migrate legacy WorldScript feature flags once, then drop the old key. + if (!stored) { + const legacy = localStorage.getItem(LEGACY_FEATURE_FLAGS_STORAGE_KEY); + if (legacy) { + localStorage.setItem(FEATURE_FLAGS_STORAGE_KEY, legacy); + localStorage.removeItem(LEGACY_FEATURE_FLAGS_STORAGE_KEY); + stored = legacy; + } + } + if (!stored) { return defaultFeatureFlagsState; } diff --git a/features/lora/loraSlice.ts b/features/lora/loraSlice.ts index 0ed33d12a..8323f0012 100644 --- a/features/lora/loraSlice.ts +++ b/features/lora/loraSlice.ts @@ -272,10 +272,10 @@ export const { export default loraSlice.reducer; // --------------------------------------------------------------------------- -// Persistence middleware (localStorage key: 'storycraft-lora') +// Persistence middleware (localStorage key: 'worldscript-lora') // --------------------------------------------------------------------------- -const LORA_STORAGE_KEY = 'storycraft-lora'; +const LORA_STORAGE_KEY = 'worldscript-lora'; type StateWithLora = { lora: LoraState }; diff --git a/features/lora/loraThunks.ts b/features/lora/loraThunks.ts index 7bcd90f1c..d05226664 100644 --- a/features/lora/loraThunks.ts +++ b/features/lora/loraThunks.ts @@ -260,7 +260,7 @@ export const evaluateAdapterThunk = createAsyncThunk< const { testOllamaAdapterPrompt } = await import('../../services/lora/loraOllamaService'); try { adapted = await testOllamaAdapterPrompt( - `storycraft-${adapterId.slice(0, 8)}`, + `worldscript-${adapterId.slice(0, 8)}`, prompt, AbortSignal.timeout(30_000), ); diff --git a/features/mindMap/mindMapUiSlice.ts b/features/mindMap/mindMapUiSlice.ts index 813a9cfaf..e256f82ab 100644 --- a/features/mindMap/mindMapUiSlice.ts +++ b/features/mindMap/mindMapUiSlice.ts @@ -13,7 +13,7 @@ export interface MindMapUiState { editingNodeId: string | null; } -const STORAGE_KEY = 'storycraft-mind-map-ui'; +const STORAGE_KEY = 'worldscript-mind-map-ui'; const defaultState: MindMapUiState = { activeMindMapId: null, diff --git a/features/plotBoard/plotBoardSlice.ts b/features/plotBoard/plotBoardSlice.ts index 765d512b2..05c0483d8 100644 --- a/features/plotBoard/plotBoardSlice.ts +++ b/features/plotBoard/plotBoardSlice.ts @@ -18,7 +18,7 @@ export interface PlotBoardState { snapToGrid: boolean; } -const STORAGE_KEY = 'storycraft-plot-board'; +const STORAGE_KEY = 'worldscript-plot-board'; const defaultState: PlotBoardState = { activeMode: 'swimlane', diff --git a/features/proForge/types.ts b/features/proForge/types.ts index 6d02187ee..042e92dfd 100644 --- a/features/proForge/types.ts +++ b/features/proForge/types.ts @@ -1,5 +1,5 @@ /** - * Core types for the StoryCraft ProForge Ultimate Author Pipeline. + * Core types for the WorldScript ProForge Ultimate Author Pipeline. * QNBS-v3: Strict typing for the 8-stage agentic pipeline with Human-in-the-Loop gates. */ diff --git a/features/progressTracker/progressTrackerSlice.ts b/features/progressTracker/progressTrackerSlice.ts index 22cd32550..d4d4c75e4 100644 --- a/features/progressTracker/progressTrackerSlice.ts +++ b/features/progressTracker/progressTrackerSlice.ts @@ -21,7 +21,7 @@ export interface ProgressTrackerState { totalWordsAllTime: number; } -const STORAGE_KEY = 'storycraft-progress-tracker'; +const STORAGE_KEY = 'worldscript-progress-tracker'; const defaultState: ProgressTrackerState = { dailyGoalWords: 500, diff --git a/features/sceneComments/sceneCommentsSlice.ts b/features/sceneComments/sceneCommentsSlice.ts index 9945a1c96..881c35d17 100644 --- a/features/sceneComments/sceneCommentsSlice.ts +++ b/features/sceneComments/sceneCommentsSlice.ts @@ -14,7 +14,7 @@ export interface SceneCommentsState { comments: ReturnType; } -const STORAGE_KEY = 'storycraft-scene-comments'; +const STORAGE_KEY = 'worldscript-scene-comments'; const loadState = (): SceneCommentsState => { if (typeof window === 'undefined') { diff --git a/features/settings/settingsSlice.ts b/features/settings/settingsSlice.ts index 19848b663..246655e7a 100644 --- a/features/settings/settingsSlice.ts +++ b/features/settings/settingsSlice.ts @@ -74,7 +74,7 @@ const defaultSettings: Settings = { localBackendPreset: 'ollama_default', openAiCompatibleBaseUrl: '', openAiSiteUrl: '', - openAiSiteTitle: 'StoryCraft Studio', + openAiSiteTitle: 'WorldScript Studio', hybridFallbackEnabled: false, hybridFallbackChain: [], ragMode: 'hybrid', @@ -163,7 +163,7 @@ const defaultSettings: Settings = { speechVolume: 1.0, allowCloudSttFallback: false, listeningTimeoutSeconds: 8, - wakeWordPhrase: 'Hey StoryCraft', + wakeWordPhrase: 'Hey WorldScript', pttShortcutId: 'voice-push-to-talk', ttsMuted: false, dictationAutoPunctuation: true, @@ -307,7 +307,7 @@ const settingsSlice = createSlice({ speechVolume: 1.0, allowCloudSttFallback: false, listeningTimeoutSeconds: 8, - wakeWordPhrase: 'Hey StoryCraft', + wakeWordPhrase: 'Hey WorldScript', pttShortcutId: 'voice-push-to-talk', ttsMuted: false, dictationAutoPunctuation: true, @@ -325,7 +325,7 @@ export const applyInitialTheme = () => { typeof localStorage.getItem === 'function'; // Fast path: read simple theme string mirrored by App.tsx - const directTheme = hasLocalStorage ? localStorage.getItem('storycraft-theme') : null; + const directTheme = hasLocalStorage ? localStorage.getItem('worldscript-theme') : null; if (directTheme === 'dark' || directTheme === 'light') { document.body.classList.remove('light-theme', 'dark-theme', 'auto-theme'); document.body.classList.add(`${directTheme}-theme`); @@ -333,7 +333,7 @@ export const applyInitialTheme = () => { } // Fallback: read from legacy serialized state (used in tests) - const storedState = hasLocalStorage ? localStorage.getItem('storycraft-state') : null; + const storedState = hasLocalStorage ? localStorage.getItem('worldscript-state') : null; if (storedState) { try { const persistedState = JSON.parse(storedState); diff --git a/hooks/useApp.ts b/hooks/useApp.ts index cb03a7c8a..73d979983 100644 --- a/hooks/useApp.ts +++ b/hooks/useApp.ts @@ -51,7 +51,7 @@ function readInitialView(): View { /* ignore */ } try { - const stored = localStorage.getItem('storycraft-last-view'); + const stored = localStorage.getItem('worldscript-last-view'); if (stored && isValidView(stored)) return stored; } catch { /* ignore */ @@ -77,11 +77,11 @@ export const useApp = ({ isNewUser }: { isNewUser: boolean }) => { function onOpenPortal() { setIsPortalActive(true); } - window.addEventListener('storycraft:openPortal', onOpenPortal); - return () => window.removeEventListener('storycraft:openPortal', onOpenPortal); + window.addEventListener('worldscript:openPortal', onOpenPortal); + return () => window.removeEventListener('worldscript:openPortal', onOpenPortal); }, []); - // QNBS-v3: web+storycraft protocol placeholder — manifest passes ?protocol= for future routing hooks. + // QNBS-v3: web+worldscript protocol placeholder — manifest passes ?protocol= for future routing hooks. useEffect(() => { try { const proto = new URLSearchParams(window.location.search).get('protocol'); @@ -108,7 +108,7 @@ export const useApp = ({ isNewUser }: { isNewUser: boolean }) => { // Save the current view to localStorage whenever it changes. useEffect(() => { try { - localStorage.setItem('storycraft-last-view', currentView); + localStorage.setItem('worldscript-last-view', currentView); } catch { /* Storage unavailable */ } diff --git a/hooks/useGlobalCopilot.ts b/hooks/useGlobalCopilot.ts index 9fe81bb84..fb8212a12 100644 --- a/hooks/useGlobalCopilot.ts +++ b/hooks/useGlobalCopilot.ts @@ -1,6 +1,6 @@ /** * useGlobalCopilot — business logic for the Global AI Copilot live assistant. - * QNBS-v3: Streams replies via useStoryCraftAI (honours the privacy/local-first AI policy), is + * QNBS-v3: Streams replies via useWorldScriptAI (honours the privacy/local-first AI policy), is * context-aware (current view + project), bridges to ProForge through the Core Capability Layer, * drives the heuristic insight generator, and supports a heuristics-only offline mode. */ @@ -36,8 +36,8 @@ import { createBrowserProForgeCapability } from '../services/proForge/adapters/b import type { ProForgeProjectSnapshot } from '../services/proForge/proForgeCapabilityCore'; import { viewNavigationLabelKey } from '../services/viewNavigationLabels'; import type { View } from '../types'; -import { useStoryCraftAI } from './useStoryCraftAI'; import { useTranslation } from './useTranslation'; +import { useWorldScriptAI } from './useWorldScriptAI'; /** Subset of the project shape the snapshot reads — avoids `any` while staying tolerant. */ interface ProjectLike { @@ -96,7 +96,7 @@ export function useGlobalCopilot(currentView: View) { const [applyStatus, setApplyStatus] = useState<'idle' | 'applying' | 'success' | 'error'>('idle'); const enableProForge = useAppSelector(selectEnableProForge); - const { runCompletion, stop, isLoading } = useStoryCraftAI({ + const { runCompletion, stop, isLoading } = useWorldScriptAI({ onIncremental: (full) => { dispatch(copilotActions.setLastAssistantContent(full)); }, @@ -232,7 +232,7 @@ export function useGlobalCopilot(currentView: View) { const open = useCallback(() => dispatch(copilotActions.setOpen(true)), [dispatch]); const close = useCallback(() => { stop(); - // QNBS-v3 (CodeAnt #7): stop() can abort the stream without firing useStoryCraftAI's + // QNBS-v3 (CodeAnt #7): stop() can abort the stream without firing useWorldScriptAI's // onFinish/onError, leaving status stuck at 'streaming' — after which sendMessage's // `status === 'streaming'` guard blocks every future send. Reset to idle on close so the // panel is usable again next time it opens. diff --git a/hooks/usePWA.ts b/hooks/usePWA.ts index 8ddf440c8..a3312610e 100644 --- a/hooks/usePWA.ts +++ b/hooks/usePWA.ts @@ -78,7 +78,7 @@ export function usePWA(): UsePWAReturn { window.addEventListener('sw-update-available', onUpdate); // ── Sync initial installed state from global object ──────── - if (window.storyCraftPWA?.isInstalled) { + if (window.worldScriptPWA?.isInstalled) { setIsInstalled(true); } // Also check via matchMedia (catches already-installed before hook mounts) @@ -89,7 +89,7 @@ export function usePWA(): UsePWAReturn { ) { setIsInstalled(true); } - if (window.storyCraftPWA?.deferredInstallPrompt) { + if (window.worldScriptPWA?.deferredInstallPrompt) { setIsInstallable(true); } @@ -103,8 +103,8 @@ export function usePWA(): UsePWAReturn { }, []); const installApp = useCallback(async () => { - if (!window.storyCraftPWA) return; - const outcome = await window.storyCraftPWA.installApp(); + if (!window.worldScriptPWA) return; + const outcome = await window.worldScriptPWA.installApp(); if (outcome === 'accepted') { setIsInstallable(false); setIsInstalled(true); @@ -130,7 +130,7 @@ export function usePWA(): UsePWAReturn { }, []); const clearCache = useCallback(async () => { - await window.storyCraftPWA?.clearCache(); + await window.worldScriptPWA?.clearCache(); }, []); // Don't show install banner if already dismissed this session diff --git a/hooks/useSettingsView.ts b/hooks/useSettingsView.ts index 97fa271fc..b2bc1a7a2 100644 --- a/hooks/useSettingsView.ts +++ b/hooks/useSettingsView.ts @@ -70,10 +70,10 @@ export const useSettingsView = () => { useEffect(() => { try { - const pending = sessionStorage.getItem('storycraft-settings-category'); + const pending = sessionStorage.getItem('worldscript-settings-category'); if (pending) { setActiveCategory(pending); - sessionStorage.removeItem('storycraft-settings-category'); + sessionStorage.removeItem('worldscript-settings-category'); } } catch { /* storage blocked */ @@ -354,7 +354,7 @@ export const useSettingsView = () => { const handleRepeatOnboarding = useCallback(() => { // QNBS-v3: useApp.ts listens for this event and re-opens the WelcomePortal. - window.dispatchEvent(new CustomEvent('storycraft:openPortal')); + window.dispatchEvent(new CustomEvent('worldscript:openPortal')); }, []); const handleCreateSnapshot = useCallback(async () => { diff --git a/hooks/useStoryCraftAI.ts b/hooks/useWorldScriptAI.ts similarity index 91% rename from hooks/useStoryCraftAI.ts rename to hooks/useWorldScriptAI.ts index fe9acbe7e..837bd1b4e 100644 --- a/hooks/useStoryCraftAI.ts +++ b/hooks/useWorldScriptAI.ts @@ -6,19 +6,19 @@ import type { RootState } from '../app/store'; import { selectEnableLoraAdapters } from '../features/featureFlags/featureFlagsSlice'; import { selectActiveLoraOllamaTag } from '../features/lora/loraSelectors'; import { - STORYCRAFT_COMPLETION_URL, - storyCraftCompletionFetch, -} from '../services/ai/storyCraftCompletionFetch'; + WORLDSCRIPT_COMPLETION_URL, + worldScriptCompletionFetch, +} from '../services/ai/worldScriptCompletionFetch'; import { createLogger, newCorrelationId } from '../services/logger'; // QNBS-v3 (Phase 1): one correlation id per AI generation, shared by the request-start log here, // the fetch-side failure log, and the retry seam — for end-to-end traceability (no PII logged). const log = createLogger('ai.completion'); -export type StoryCraftIncrementalHandler = (fullText: string, delta: string) => void; +export type WorldScriptIncrementalHandler = (fullText: string, delta: string) => void; -export interface UseStoryCraftAIOptions { - onIncremental: StoryCraftIncrementalHandler; +export interface UseWorldScriptAIOptions { + onIncremental: WorldScriptIncrementalHandler; onFinish?: (prompt: string, completion: string) => void; onError?: (error: Error) => void; } @@ -27,7 +27,7 @@ export interface UseStoryCraftAIOptions { * Vercel AI SDK (`useCompletion`) + client-side `streamText` for Writer streaming. * Redux sync via `onIncremental` / `onFinish` — not written directly into the manuscript (see `services/ai/index.ts`). */ -export function useStoryCraftAI(options: UseStoryCraftAIOptions) { +export function useWorldScriptAI(options: UseWorldScriptAIOptions) { const provider = useAppSelector((s: RootState) => s.settings.advancedAi.provider); const model = useAppSelector((s: RootState) => s.settings.advancedAi.model); const ollamaBaseUrl = useAppSelector((s: RootState) => s.settings.advancedAi.ollamaBaseUrl); @@ -83,9 +83,9 @@ export function useStoryCraftAI(options: UseStoryCraftAIOptions) { const correlationIdRef = useRef(''); const { completion, complete, stop, isLoading, error, setCompletion } = useCompletion({ - api: STORYCRAFT_COMPLETION_URL, + api: WORLDSCRIPT_COMPLETION_URL, streamProtocol: 'text', - fetch: storyCraftCompletionFetch, + fetch: worldScriptCompletionFetch, body, onFinish: (prompt, text) => { finishRef.current?.(prompt, text); diff --git a/hooks/useWriterView.ts b/hooks/useWriterView.ts index 20d67dc13..63d395a13 100644 --- a/hooks/useWriterView.ts +++ b/hooks/useWriterView.ts @@ -13,8 +13,8 @@ import { getAiErrorMessage } from '../services/ai/aiErrorTaxonomy'; import { isOrchestrationReadyProvider } from '../services/ai/orchestrationProviders'; import { logger } from '../services/logger'; import { assembleRAGPrompt } from '../services/ragPromptAssembly'; -import { useStoryCraftAI } from './useStoryCraftAI'; import { useTranslation } from './useTranslation'; +import { useWorldScriptAI } from './useWorldScriptAI'; export const useWriterView = () => { const { t, language } = useTranslation(); @@ -48,7 +48,7 @@ export const useWriterView = () => { const abortControllerRef = useRef(null); const fullStreamRef = useRef(''); - const { runCompletion, stop: stopOrchestrationStreaming } = useStoryCraftAI({ + const { runCompletion, stop: stopOrchestrationStreaming } = useWorldScriptAI({ onIncremental: useCallback( (fullText: string, delta: string) => { fullStreamRef.current = fullText; diff --git a/index.html b/index.html index bc534caa7..2281a9cc0 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - + @@ -53,7 +53,7 @@ - connect-src: 'self' + https: — intentional broad HTTPS scheme-source. REQUIRED by the shipped BYOK feature `openAiCompatibleBaseUrl` (Settings → AI → - custom base URL): users point StoryCraft at arbitrary self-hosted + custom base URL): users point WorldScript at arbitrary self-hosted or third-party OpenAI-compatible proxies that cannot be statically enumerated in a meta CSP. Because `https:` already covers every HTTPS origin, the explicit cloud-provider endpoints (Gemini, diff --git a/index.tsx b/index.tsx index 1d918305c..1e443585e 100644 --- a/index.tsx +++ b/index.tsx @@ -68,7 +68,7 @@ function renderStartupError(message: string) { const rootEl = document.getElementById('root'); if (!rootEl || rootEl.childElementCount > 0) return; const safe = String(message).replace(/[<>&]/g, (c) => `&#${c.charCodeAt(0)};`); - rootEl.innerHTML = `

StoryCraft Studio

A critical startup error occurred. Please reload the page.

${safe}
`; + rootEl.innerHTML = `

WorldScript Studio

A critical startup error occurred. Please reload the page.

${safe}
`; } window.addEventListener('error', (event) => { @@ -102,7 +102,7 @@ function StorageErrorScreen({ message, onReset }: { message: string; onReset: () gap: '1rem', }} > -

StoryCraft Studio

+

WorldScript Studio

The local database could not be opened. This can happen after a browser update or when storage is full. diff --git a/packages/ai-core/src/index.ts b/packages/ai-core/src/index.ts index d0c008dd0..cde65dcc8 100644 --- a/packages/ai-core/src/index.ts +++ b/packages/ai-core/src/index.ts @@ -233,7 +233,7 @@ async function runWebLlmLayer( signal?: AbortSignal, ): Promise { if (signal?.aborted) throw new Error('Aborted'); - // QNBS-v3: Only one tab loads WebLLM — avoids GPU/RAM collision across multiple StoryCraft tabs. + // QNBS-v3: Only one tab loads WebLLM — avoids GPU/RAM collision across multiple WorldScript tabs. if (!hasWebGpu || !gpuTabLeader) return null; const mod = await import('./vendor-webllm'); type EngineModule = typeof mod & { @@ -344,7 +344,7 @@ export async function runLocalTextGeneration( return { layer: 'webllm', text: - 'WebLLM: Another StoryCraft tab holds the local inference lock. Close extra tabs or use Ollama. Preview: ' + + 'WebLLM: Another WorldScript tab holds the local inference lock. Close extra tabs or use Ollama. Preview: ' + sanitizedPrompt.slice(0, 160) + (sanitizedPrompt.length > 160 ? '…' : ''), }; diff --git a/packages/ai-core/src/tabLeaderElection.ts b/packages/ai-core/src/tabLeaderElection.ts index 2c9bf2778..fd579abb9 100644 --- a/packages/ai-core/src/tabLeaderElection.ts +++ b/packages/ai-core/src/tabLeaderElection.ts @@ -4,8 +4,8 @@ * QNBS-v3: localStorage heartbeat for fast-path detection of alive leaders across reloads. */ -const CHANNEL_NAME = 'storycraft-local-ai-tab-leader-v1'; -const HEARTBEAT_KEY = 'storycraft-ai-leader-heartbeat'; +const CHANNEL_NAME = 'worldscript-local-ai-tab-leader-v1'; +const HEARTBEAT_KEY = 'worldscript-ai-leader-heartbeat'; // QNBS-v3: Heartbeat refreshed every 5s; leader considered stale after 2.4× that (12s), // giving enough slack for background-throttled tabs. const HEARTBEAT_INTERVAL_MS = 5_000; diff --git a/packages/collab-transport/src/crypto.js b/packages/collab-transport/src/crypto.js index 4ef60fbf5..71a91f53e 100644 --- a/packages/collab-transport/src/crypto.js +++ b/packages/collab-transport/src/crypto.js @@ -3,7 +3,7 @@ * Upstream: https://github.com/yjs/y-webrtc | npm tag: v10.3.0 * Package: @domain/collab-transport v10.3.0-sc1 (vendored 2026-05-28) * - * StoryCraft patches (C-1, 2026-05-28 — commit 63afa69): + * WorldScript patches (C-1, 2026-05-28 — commit 63afa69): * 1. PBKDF2 iterations raised 100k → 310k → 600k (OWASP 2024 SHA-256 minimum) * 2. deriveKey: extractable=false (prevents key export via subtle.exportKey) * 3. encryptMessageContent: added `return` before promise.reject() (was silent swallow) @@ -11,7 +11,7 @@ * SECURITY MAINTENANCE — Renovate cannot auto-update this fork. * On any new y-webrtc release: diff crypto.js + y-webrtc.js against the new tag, * cherry-pick security fixes, re-apply SC patches 1-3 above, bump version to -sc1. - * Checklist + audit log: https://github.com/qnbs/StoryCraft-Studio/issues/60 + * Checklist + audit log: https://github.com/qnbs/WorldScript-Studio/issues/60 */ /* eslint-env browser */ diff --git a/packages/collab-transport/src/index.ts b/packages/collab-transport/src/index.ts index 48e9b7ce2..58aa47617 100644 --- a/packages/collab-transport/src/index.ts +++ b/packages/collab-transport/src/index.ts @@ -1,4 +1,4 @@ -// QNBS-v3: B-3 vendor fork — y-webrtc 10.3.0 with StoryCraft RTCDataChannel E2E encryption patch. +// QNBS-v3: B-3 vendor fork — y-webrtc 10.3.0 with WorldScript RTCDataChannel E2E encryption patch. // Replaces patchedDependencies approach; patch is permanently baked into this package's JS source. // TypeScript resolves types via the adjacent y-webrtc.d.ts (moduleResolution: bundler). diff --git a/packages/ui/src/tokens.ts b/packages/ui/src/tokens.ts index fd27644ef..bb387c24f 100644 --- a/packages/ui/src/tokens.ts +++ b/packages/ui/src/tokens.ts @@ -1,5 +1,5 @@ /** - * StoryCraft semantic design tokens — mirror `index.css` CSS custom properties. + * WorldScript semantic design tokens — mirror `index.css` CSS custom properties. * QNBS-v3: Single TS source for Storybook/docs; runtime styling uses CSS vars. */ export const designTokens = { diff --git a/packages/ui/tailwind-preset.ts b/packages/ui/tailwind-preset.ts index 4801aacc2..b56e5a702 100644 --- a/packages/ui/tailwind-preset.ts +++ b/packages/ui/tailwind-preset.ts @@ -1,7 +1,7 @@ import type { Config } from 'tailwindcss'; /** Matches `index.css` semantic vars — utilities like `bg-sc-accent`, `rounded-sc-lg`. */ -export const storycraftTailwindPreset: Partial = { +export const worldscriptTailwindPreset: Partial = { theme: { extend: { colors: { diff --git a/packages/worker-bus/src/deadLetterQueue.ts b/packages/worker-bus/src/deadLetterQueue.ts index 93fe6d90a..cd92fc0a2 100644 --- a/packages/worker-bus/src/deadLetterQueue.ts +++ b/packages/worker-bus/src/deadLetterQueue.ts @@ -6,7 +6,7 @@ import { DEAD_LETTER_CAPACITY } from './constants'; import type { TaskResult, WorkerTask } from './types'; const log = createLogger('worker-bus:dlq'); -const IDB_DB_NAME = 'storycraft-dead-letter-db'; +const IDB_DB_NAME = 'worldscript-dead-letter-db'; const IDB_STORE = 'dead_letters'; export interface DeadLetterEntry { diff --git a/plugins/example-word-counter/index.ts b/plugins/example-word-counter/index.ts index 6c2a2f826..9c9711c32 100644 --- a/plugins/example-word-counter/index.ts +++ b/plugins/example-word-counter/index.ts @@ -1,5 +1,5 @@ /** - * Example StoryCraft Plugin — Word Counter. + * Example WorldScript Plugin — Word Counter. * QNBS-v3: Demonstrates scene.read + storage.read/write APIs. * Install via: pluginRegistry.registerWithValidation(descriptor) */ diff --git a/register-sw.ts b/register-sw.ts index ebcd9efc8..a5c9ba772 100644 --- a/register-sw.ts +++ b/register-sw.ts @@ -1,10 +1,10 @@ import { logger as appLogger } from './services/logger'; // ============================================================ -// StoryCraft Studio — Service Worker Registration v3.0 +// WorldScript Studio — Service Worker Registration v3.0 // Features: // • Update detection + explicit user-triggered skipWaiting -// • beforeinstallprompt capture → window.storyCraftPWA +// • beforeinstallprompt capture → window.worldScriptPWA // • appinstalled tracking // • Periodic background sync registration // • Custom events: sw-update-available, sw-installed @@ -17,7 +17,7 @@ export interface PWAInstallEvent extends Event { declare global { interface Window { - storyCraftPWA: { + worldScriptPWA: { deferredInstallPrompt: PWAInstallEvent | null; isInstalled: boolean; swRegistration: ServiceWorkerRegistration | null; @@ -30,22 +30,22 @@ declare global { } // ── Global PWA state object ─────────────────────────────────── -window.storyCraftPWA = { +window.worldScriptPWA = { deferredInstallPrompt: null, isInstalled: false, swRegistration: null, async installApp() { - const prompt = window.storyCraftPWA.deferredInstallPrompt; + const prompt = window.worldScriptPWA.deferredInstallPrompt; if (!prompt) return 'unavailable'; await prompt.prompt(); const { outcome } = await prompt.userChoice; - window.storyCraftPWA.deferredInstallPrompt = null; + window.worldScriptPWA.deferredInstallPrompt = null; return outcome; }, async checkForUpdate() { - const reg = window.storyCraftPWA.swRegistration; + const reg = window.worldScriptPWA.swRegistration; if (reg) await reg.update(); }, @@ -63,13 +63,13 @@ window.storyCraftPWA = { // ── Capture install prompt before browser auto-dismisses it ── window.addEventListener('beforeinstallprompt', (e) => { e.preventDefault(); - window.storyCraftPWA.deferredInstallPrompt = e as PWAInstallEvent; + window.worldScriptPWA.deferredInstallPrompt = e as PWAInstallEvent; window.dispatchEvent(new CustomEvent('sw-installable', { detail: { installable: true } })); }); window.addEventListener('appinstalled', () => { - window.storyCraftPWA.isInstalled = true; - window.storyCraftPWA.deferredInstallPrompt = null; + window.worldScriptPWA.isInstalled = true; + window.worldScriptPWA.deferredInstallPrompt = null; window.dispatchEvent(new CustomEvent('sw-installed')); appLogger.info('[PWA] App installed successfully'); }); @@ -103,7 +103,7 @@ const registerServiceWorker = async (): Promise => { updateViaCache: 'none', // always fetch new SW from network }); - window.storyCraftPWA.swRegistration = registration; + window.worldScriptPWA.swRegistration = registration; appLogger.info('[SW] Registered, scope:', registration.scope); // ── Detect and announce SW updates ─────────────────────── @@ -160,7 +160,7 @@ const registerServiceWorker = async (): Promise => { }); if (status.state === 'granted') { // @ts-expect-error — periodicSync not in TS lib yet - await registration.periodicSync.register('storycraft-refresh', { + await registration.periodicSync.register('worldscript-refresh', { minInterval: 24 * 60 * 60 * 1000, // once per day }); } @@ -175,7 +175,7 @@ const registerServiceWorker = async (): Promise => { // @ts-expect-error — iOS Safari proprietary window.navigator.standalone === true ) { - window.storyCraftPWA.isInstalled = true; + window.worldScriptPWA.isInstalled = true; } } catch (error) { appLogger.error('[SW] Registration failed:', error); diff --git a/scripts/audit-feature-parity.ts b/scripts/audit-feature-parity.ts index 2e1b9a3dc..c67c7f53a 100644 --- a/scripts/audit-feature-parity.ts +++ b/scripts/audit-feature-parity.ts @@ -148,7 +148,7 @@ const handlerFlags = extractHandlerFlags(hookSrc); let errors = 0; let warnings = 0; -console.log(bold('\n=== StoryCraft Studio — Feature Parity Audit ===\n')); +console.log(bold('\n=== WorldScript Studio — Feature Parity Audit ===\n')); console.log(`Found ${sliceFlags.length} flags in FeatureFlagsState\n`); const rows: Array<{ diff --git a/scripts/cf-pages-deploy.mjs b/scripts/cf-pages-deploy.mjs index 556dc30ce..dc8bb8c56 100644 --- a/scripts/cf-pages-deploy.mjs +++ b/scripts/cf-pages-deploy.mjs @@ -43,7 +43,7 @@ if (!fs.existsSync(path.join(dist, 'index.html'))) { process.exit(1); } -const project = process.env.CLOUDFLARE_PAGES_PROJECT ?? 'storycraft-studio'; +const project = process.env.CLOUDFLARE_PAGES_PROJECT ?? 'worldscript-studio'; const args = [ 'pages', 'deploy', diff --git a/scripts/check-i18n-keys.mjs b/scripts/check-i18n-keys.mjs index 008dbcf21..457d364ee 100644 --- a/scripts/check-i18n-keys.mjs +++ b/scripts/check-i18n-keys.mjs @@ -92,7 +92,7 @@ const quality = process.argv.includes('--quality'); // Patterns for values that are legitimately the same across languages const SKIP_PATTERNS = [ /^(PDF|DOCX?|HTML|RTF|EPUB|JSON|CSV|TXT|ZIP|PNG|JPG|SVG|MD)(\s|$|\s*\()/i, - /^(Gemini|OpenAI|Ollama|Claude|GPT|API|URL|HTTP|HTTPS|WebRTC|WebSocket|Yjs|IndexedDB|LZ-String|AES-256|CRDT|PWA|Tauri|GitHub|Google\s|Discord|LM Studio|vLLM|WebLLM|Dropbox|OneDrive|iCloud|StoryCraft)/i, + /^(Gemini|OpenAI|Ollama|Claude|GPT|API|URL|HTTP|HTTPS|WebRTC|WebSocket|Yjs|IndexedDB|LZ-String|AES-256|CRDT|PWA|Tauri|GitHub|Google\s|Discord|LM Studio|vLLM|WebLLM|Dropbox|OneDrive|iCloud|WorldScript)/i, /^Ctrl\+|^Alt\+|^Shift\+|^Meta\+|^\+\s/, /^\d+(\.\d+)?(\s*(KB|MB|GB|px|ms|s|%))?$/, /^v\d+\.\d+/, diff --git a/scripts/resolve-deploy-base.mjs b/scripts/resolve-deploy-base.mjs index ace7fb529..d8cb172e3 100644 --- a/scripts/resolve-deploy-base.mjs +++ b/scripts/resolve-deploy-base.mjs @@ -1,7 +1,7 @@ #!/usr/bin/env node /** * Resolves Vite `base` for static hosting targets. - * - GitHub Pages (default): /StoryCraft-Studio/ + * - GitHub Pages (default): /WorldScript-Studio/ * - Vercel / Cloudflare Pages (root domain): set VITE_BASE=/ or DEPLOY_TARGET=edge */ function resolveDeployBase() { @@ -12,7 +12,7 @@ function resolveDeployBase() { if (process.env.DEPLOY_TARGET === 'edge') { return '/'; } - return '/StoryCraft-Studio/'; + return '/WorldScript-Studio/'; } export const deployBase = resolveDeployBase(); diff --git a/scripts/smoke-prod-build.mjs b/scripts/smoke-prod-build.mjs index 3dca237a3..5664d5108 100644 --- a/scripts/smoke-prod-build.mjs +++ b/scripts/smoke-prod-build.mjs @@ -14,7 +14,7 @@ import { setTimeout as sleep } from 'node:timers/promises'; import { chromium } from '@playwright/test'; const PORT = Number(process.env['SMOKE_PORT'] ?? 4173); -const CANDIDATE_PATHS = ['/StoryCraft-Studio/', '/']; +const CANDIDATE_PATHS = ['/WorldScript-Studio/', '/']; function startPreview() { const child = spawn( diff --git a/scripts/sync-deploy-base.mjs b/scripts/sync-deploy-base.mjs index 798c7e45d..29089e585 100644 --- a/scripts/sync-deploy-base.mjs +++ b/scripts/sync-deploy-base.mjs @@ -1,6 +1,6 @@ #!/usr/bin/env node /** - * Patches public assets that hardcode /StoryCraft-Studio/ for edge (root) deploys. + * Patches public assets that hardcode /WorldScript-Studio/ for edge (root) deploys. * GitHub Pages CI leaves defaults; Vercel/Cloudflare set DEPLOY_TARGET=edge or VITE_BASE=/. */ import fs from 'node:fs'; @@ -10,7 +10,7 @@ import { deployBase } from './resolve-deploy-base.mjs'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const root = path.resolve(__dirname, '..'); -const GITHUB_PAGES_BASE = '/StoryCraft-Studio/'; +const GITHUB_PAGES_BASE = '/WorldScript-Studio/'; if (deployBase === GITHUB_PAGES_BASE) { console.log('[sync-deploy-base] GitHub Pages base — no public patches'); diff --git a/services/ai/aiInferenceCacheService.ts b/services/ai/aiInferenceCacheService.ts index d7e47be23..6863f218b 100644 --- a/services/ai/aiInferenceCacheService.ts +++ b/services/ai/aiInferenceCacheService.ts @@ -1,5 +1,5 @@ // QNBS-v3: Two-layer inference cache — in-memory LRU for hot paths, IndexedDB for persistence. -// Adapted from CannaGuide-2025 cacheService.ts patterns for StoryCraft creative context. +// Adapted from CannaGuide-2025 cacheService.ts patterns for WorldScript creative context. const IN_MEMORY_MAX = 64; const IDB_MAX_ENTRIES = 256; @@ -7,7 +7,7 @@ const TTL_MS = 7 * 24 * 60 * 60 * 1000; // 7 days // QNBS-v3: Skip caching for long prompts — they're likely unique streaming contexts. const SKIP_CACHE_PROMPT_LENGTH = 512; const IDB_STORE = 'inference-cache'; -const IDB_DB_NAME = 'storycraft-inference-cache-db'; +const IDB_DB_NAME = 'worldscript-inference-cache-db'; const IDB_DB_VERSION = 1; interface CacheEntry { diff --git a/services/ai/benchmarkService.ts b/services/ai/benchmarkService.ts index 9f0042067..46b4d92f5 100644 --- a/services/ai/benchmarkService.ts +++ b/services/ai/benchmarkService.ts @@ -8,7 +8,7 @@ import { logger as log } from '../logger'; import { type AiTaskType, adaptiveAiEngine } from './adaptiveAiEngine'; import type { ComputeBackend } from './localAiDeviceProfiler'; -const BENCHMARK_STORAGE_KEY = 'storycraft-benchmarks'; +const BENCHMARK_STORAGE_KEY = 'worldscript-benchmarks'; const MAX_STORED_RESULTS = 50; // Number of benchmark runs per task (first run discarded as warm-up) const BENCHMARK_RUNS = 3; diff --git a/services/ai/ecoModeService.ts b/services/ai/ecoModeService.ts index c848cfd97..b33df258f 100644 --- a/services/ai/ecoModeService.ts +++ b/services/ai/ecoModeService.ts @@ -1,5 +1,5 @@ // QNBS-v3: Eco mode — auto-detects low battery / low-end device and forces lightweight inference. -// Adapted from CannaGuide-2025 ecoModeService.ts for StoryCraft context. +// Adapted from CannaGuide-2025 ecoModeService.ts for WorldScript context. const LOW_BATTERY_THRESHOLD = 0.2; // < 20% → eco mode const CRITICAL_BATTERY_THRESHOLD = 0.1; // < 10% → critical (block large downloads) diff --git a/services/ai/fetchAdapter.ts b/services/ai/fetchAdapter.ts index 86e623aa3..ee3cbdef5 100644 --- a/services/ai/fetchAdapter.ts +++ b/services/ai/fetchAdapter.ts @@ -19,7 +19,7 @@ async function resolveTauriFetch(): Promise { } } -export interface StoryCraftFetchOptions { +export interface WorldScriptFetchOptions { /** * QNBS-v3: P1-F6 — opt-in request timeout (ms). DEFAULT OFF: streaming AI calls must not be * aborted mid-stream, so the timeout is only applied when a caller explicitly sets it (use for @@ -68,11 +68,11 @@ function buildTimeoutSignal( * Fetch für AI-Provider: im **Tauri-Desktop** Rust-HTTP-Client (CORS-Umgehung für lokale LLMs), * sonst Browser-`fetch`. Schlägt das Plugin fehl → Fallback auf `globalThis.fetch`. * - * QNBS-v3: `options.timeoutMs` is opt-in (see {@link StoryCraftFetchOptions}); without it the + * QNBS-v3: `options.timeoutMs` is opt-in (see {@link WorldScriptFetchOptions}); without it the * returned fetch is behaviourally identical to a bare `fetch`, so existing streaming callers are * unaffected. When set, the timeout applies consistently via {@link buildTimeoutSignal}. */ -export function createStoryCraftFetch(options?: StoryCraftFetchOptions): FetchLike { +export function createWorldScriptFetch(options?: WorldScriptFetchOptions): FetchLike { const timeoutMs = options?.timeoutMs; return async (input: RequestInfo | URL, init?: RequestInit) => { const tauriFetch = await resolveTauriFetch(); diff --git a/services/ai/index.ts b/services/ai/index.ts index 46ab901f8..a30d38989 100644 --- a/services/ai/index.ts +++ b/services/ai/index.ts @@ -8,7 +8,7 @@ * ## Thunk-Mapping (Legacy → Vercel AI SDK) * | Bereich | Legacy-Thunk | Neu | * |---------|----------------|-----| - * | Writer | `streamGenerationThunk` | `useStoryCraftAI` + `useCompletion` + `streamText` | + * | Writer | `streamGenerationThunk` | `useWorldScriptAI` + `useCompletion` + `streamText` | * | Synopsis / Text | `generateSynopsisThunk`, Feld-Regenerate | `generateText` (folgende Phasen) | * | JSON / Schema | `generate*Thunk` mit `generateJson` | `generateObject` (folgende Phasen) | * | Hilfe | `streamAiHelpResponse` | `streamText` / Hook (optional) | @@ -55,7 +55,7 @@ export { isMemoryPressured, } from './deviceHealthService'; export { ECO_MODE_MODEL_ID, ecoModeService } from './ecoModeService'; -export { createStoryCraftFetch } from './fetchAdapter'; +export { createWorldScriptFetch } from './fetchAdapter'; export { type GpuConsumer, type GpuPriority, @@ -100,9 +100,9 @@ export { validateOpenRouterKey, } from './openrouterModels'; export { - createLanguageModelForStoryCraft, + createLanguageModelForWorldScript, providerToKind, - type StoryCraftLanguageModelConfig, + type WorldScriptLanguageModelConfig, } from './providerFactory'; export { getApproxRpm, @@ -112,4 +112,7 @@ export { resetOpenRouterCircuit, } from './providers/openrouterProvider'; export { logRoutingDecision, type RoutingDecision, type RoutingReason } from './routingLogger'; -export { STORYCRAFT_COMPLETION_URL, storyCraftCompletionFetch } from './storyCraftCompletionFetch'; +export { + WORLDSCRIPT_COMPLETION_URL, + worldScriptCompletionFetch, +} from './worldScriptCompletionFetch'; diff --git a/services/ai/inferenceProgressEmitter.ts b/services/ai/inferenceProgressEmitter.ts index 0419440fd..1eea7b580 100644 --- a/services/ai/inferenceProgressEmitter.ts +++ b/services/ai/inferenceProgressEmitter.ts @@ -1,6 +1,6 @@ // QNBS-v3: Pub/sub progress emitter for WebLLM model downloads — decoupled from Redux so // the UI can subscribe without dispatching on every 1% progress tick. -// Adapted from CannaGuide-2025 progressEmitter.ts for StoryCraft context. +// Adapted from CannaGuide-2025 progressEmitter.ts for WorldScript context. export type WebLlmLoadingState = 'idle' | 'loading' | 'ready' | 'error'; diff --git a/services/ai/openrouterModels.ts b/services/ai/openrouterModels.ts index 041192475..0e95bedc4 100644 --- a/services/ai/openrouterModels.ts +++ b/services/ai/openrouterModels.ts @@ -14,7 +14,7 @@ const logger = createLogger('openrouter-models'); const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'; const MODELS_URL = `${OPENROUTER_BASE_URL}/models`; -const CACHE_KEY = 'storycraft-openrouter-models'; +const CACHE_KEY = 'worldscript-openrouter-models'; const CACHE_TTL_MS = 60 * 60 * 1000; // 1 hour export interface OpenRouterModel { diff --git a/services/ai/providerFactory.ts b/services/ai/providerFactory.ts index 66b1516b6..b9960f64e 100644 --- a/services/ai/providerFactory.ts +++ b/services/ai/providerFactory.ts @@ -3,10 +3,10 @@ import { createOpenAI } from '@ai-sdk/openai'; import type { LanguageModel } from 'ai'; import type { AIProvider } from '../../types'; -import { createStoryCraftFetch } from './fetchAdapter'; +import { createWorldScriptFetch } from './fetchAdapter'; /** Konfiguration zur Erzeugung eines `LanguageModel` (erweiterbar um WebLLM o. Ä.). */ -export type StoryCraftLanguageModelConfig = +export type WorldScriptLanguageModelConfig = | { provider: 'gemini'; /** Gemini-Modell-ID (z. B. `gemini-3.5-flash`). */ @@ -33,15 +33,15 @@ export type StoryCraftLanguageModelConfig = }; function resolveFetch(): typeof globalThis.fetch { - return createStoryCraftFetch() as typeof globalThis.fetch; + return createWorldScriptFetch() as typeof globalThis.fetch; } /** * Erzeugt ein Vercel-AI-SDK-`LanguageModel` — Provider-frei für `streamText` / `generateText`. * QNBS-v3: zentrale Fabrik für Gemini + OpenAI-kompatibel (lokal); WebLLM später als eigene Union-Verzweigung. */ -export function createLanguageModelForStoryCraft( - config: StoryCraftLanguageModelConfig, +export function createLanguageModelForWorldScript( + config: WorldScriptLanguageModelConfig, ): LanguageModel { const fetchImpl = resolveFetch(); @@ -73,7 +73,7 @@ export function createLanguageModelForStoryCraft( } } -/** Mappt Redux-`AIProvider` auf Fabrik-Union (ohne Keys — Auflösung in `storyCraftCompletionFetch`). */ +/** Mappt Redux-`AIProvider` auf Fabrik-Union (ohne Keys — Auflösung in `worldScriptCompletionFetch`). */ export function providerToKind( provider: AIProvider, ): 'gemini' | 'openai' | 'openaiCompatible' | 'unsupported' { diff --git a/services/ai/providers/openrouterProvider.ts b/services/ai/providers/openrouterProvider.ts index 63a21a721..0195af5fd 100644 --- a/services/ai/providers/openrouterProvider.ts +++ b/services/ai/providers/openrouterProvider.ts @@ -26,8 +26,8 @@ import { isOpenRouterFreeModel } from '../openrouterModels'; const logger = createLogger('openrouter-provider'); const OPENROUTER_BASE_URL = 'https://openrouter.ai/api/v1'; -const SITE_URL = 'https://github.com/qnbs/StoryCraft-Studio'; -const SITE_TITLE = 'StoryCraft Studio'; +const SITE_URL = 'https://github.com/qnbs/WorldScript-Studio'; +const SITE_TITLE = 'WorldScript Studio'; // ─── Free-tier model catalog ───────────────────────────────────────────────── @@ -49,7 +49,7 @@ export { isOpenRouterFreeModel }; const MAX_CONSECUTIVE_429 = 4; const CIRCUIT_OPEN_MS = 5 * 60 * 1000; // 5 minutes -const CB_STORAGE_KEY = 'storycraft-or-cb-state'; +const CB_STORAGE_KEY = 'worldscript-or-cb-state'; // QNBS-v3: Only the pause deadline is persisted. The "consecutive" 429 counter is deliberately // NOT persisted — restoring it would let a stale count survive a long idle period so that a single diff --git a/services/ai/telemetryService.ts b/services/ai/telemetryService.ts index d483fb118..5bce3d817 100644 --- a/services/ai/telemetryService.ts +++ b/services/ai/telemetryService.ts @@ -17,7 +17,7 @@ export function setTelemetryEnabled(enabled: boolean): void { _telemetryEnabled = enabled; } -const TELEMETRY_STORAGE_KEY = 'storycraft-ai-telemetry'; +const TELEMETRY_STORAGE_KEY = 'worldscript-ai-telemetry'; const MAX_LOCAL_ENTRIES = 200; // DuckDB table DDL — created on first write diff --git a/services/ai/storyCraftCompletionFetch.ts b/services/ai/worldScriptCompletionFetch.ts similarity index 89% rename from services/ai/storyCraftCompletionFetch.ts rename to services/ai/worldScriptCompletionFetch.ts index 893ebe65d..e3036a532 100644 --- a/services/ai/storyCraftCompletionFetch.ts +++ b/services/ai/worldScriptCompletionFetch.ts @@ -12,9 +12,9 @@ import { normalizeOpenAiCompatibleBaseUrl, } from './modelNormalization'; import { - createLanguageModelForStoryCraft, + createLanguageModelForWorldScript, providerToKind, - type StoryCraftLanguageModelConfig, + type WorldScriptLanguageModelConfig, } from './providerFactory'; const aiProviderSchema = z.enum([ @@ -40,9 +40,9 @@ const completionBodySchema = z.object({ openAiSiteUrl: z.string().optional(), openAiSiteTitle: z.string().optional(), // QNBS-v3: C-3 LoRA wiring — when enableLoraAdapters is on and an adapter has ollamaModelTag, - // useStoryCraftAI passes it here so the Ollama model identifier is overridden at inference time. + // useWorldScriptAI passes it here so the Ollama model identifier is overridden at inference time. loraModelPath: z.string().optional(), - // QNBS-v3 (Phase 1): opaque per-request correlation id propagated from useStoryCraftAI so the + // QNBS-v3 (Phase 1): opaque per-request correlation id propagated from useWorldScriptAI so the // client request log and this fetch-side failure log share one id. Never user-derived. correlationId: z.string().optional(), }); @@ -58,8 +58,8 @@ function readCorrelationId(raw: unknown): string | undefined { return undefined; } -/** Virtuelle URL — nur für `useCompletion`; der echte Transport läuft über `storyCraftCompletionFetch`. */ -export const STORYCRAFT_COMPLETION_URL = 'storycraft-internal://completion'; +/** Virtuelle URL — nur für `useCompletion`; der echte Transport läuft über `worldScriptCompletionFetch`. */ +export const WORLDSCRIPT_COMPLETION_URL = 'worldscript-internal://completion'; async function resolveModelConfig( provider: AIProvider, @@ -70,7 +70,7 @@ async function resolveModelConfig( openAiSiteUrl?: string; openAiSiteTitle?: string; }, -): Promise { +): Promise { const kind = providerToKind(provider); if (kind === 'unsupported') { return { @@ -128,14 +128,14 @@ async function resolveModelConfig( * und liefert eine Text-Stream-Response — ohne separates Backend. */ // QNBS-v3: useCompletion verlangt fetch(URL) — Stream läuft clientseitig; URL-Parameter bleibt ungenutzt. -export async function storyCraftCompletionFetch( +export async function worldScriptCompletionFetch( _input: RequestInfo | URL, init?: RequestInit, ): Promise { let correlationId: string | undefined; try { if (!init?.body || typeof init.body !== 'string') { - throw new Error('Invalid StoryCraft AI request body.'); + throw new Error('Invalid WorldScript AI request body.'); } const raw: unknown = JSON.parse(init.body); correlationId = readCorrelationId(raw); @@ -179,7 +179,7 @@ export async function storyCraftCompletionFetch( }); } - const model = createLanguageModelForStoryCraft(resolved); + const model = createLanguageModelForWorldScript(resolved); const temperature = CREATIVITY_TO_TEMPERATURE[parsed.creativity as AiCreativity]; const maxOutputTokens = parsed.maxOutputTokens ?? 2048; @@ -203,8 +203,8 @@ export async function storyCraftCompletionFetch( }); } // QNBS-v3: never expose err.message in response body — may contain internal paths or tokens (CodeQL js/stack-trace-exposure) - log.withContext({ correlationId }).error('storyCraftCompletionFetch failed', err); - return new Response(JSON.stringify({ error: 'StoryCraft AI request failed.' }), { + log.withContext({ correlationId }).error('worldScriptCompletionFetch failed', err); + return new Response(JSON.stringify({ error: 'WorldScript AI request failed.' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }); diff --git a/services/aiProviderService.ts b/services/aiProviderService.ts index 49febb720..52409ef17 100644 --- a/services/aiProviderService.ts +++ b/services/aiProviderService.ts @@ -625,8 +625,8 @@ export async function streamAiHelpResponse( ? `${doc}\n\n---\n\nUser question:\n${sanitizePromptValue(question)}` : sanitizePromptValue(question); const helpPromptWithDocs = doc - ? `You are a helpful assistant for StoryCraft Studio. Prefer the documentation excerpts below when they answer the question; otherwise give concise general guidance. Format using Markdown.\n\n${mergedBody}` - : `You are a helpful assistant for a creative writing app called StoryCraft Studio. Answer the user's question concisely and clearly. Format your answer using Markdown. Question: ${sanitizePromptValue(question)}`; + ? `You are a helpful assistant for WorldScript Studio. Prefer the documentation excerpts below when they answer the question; otherwise give concise general guidance. Format using Markdown.\n\n${mergedBody}` + : `You are a helpful assistant for a creative writing app called WorldScript Studio. Answer the user's question concisely and clearly. Format your answer using Markdown. Question: ${sanitizePromptValue(question)}`; if (opts.provider === 'gemini') { return streamAiHelpResponseGemini( mergedBody, diff --git a/services/cloudSync/cloudSyncEncryption.ts b/services/cloudSync/cloudSyncEncryption.ts index c892657e9..c160bb842 100644 --- a/services/cloudSync/cloudSyncEncryption.ts +++ b/services/cloudSync/cloudSyncEncryption.ts @@ -6,7 +6,7 @@ const IV_LENGTH = 12; /** Derives a per-user AES-256-GCM key from a passphrase + deterministic salt (userId). */ export async function deriveCloudSyncKey(passphrase: string, userId: string): Promise { const encoded = new TextEncoder().encode(passphrase); - const saltInput = new TextEncoder().encode(`storycraft-cloud-sync::${userId}`); + const saltInput = new TextEncoder().encode(`worldscript-cloud-sync::${userId}`); const salt = await crypto.subtle.digest('SHA-256', saltInput); const keyMaterial = await crypto.subtle.importKey('raw', encoded, 'PBKDF2', false, ['deriveKey']); diff --git a/services/collaborationService.ts b/services/collaborationService.ts index 1a17c07d9..acd208fc5 100644 --- a/services/collaborationService.ts +++ b/services/collaborationService.ts @@ -130,7 +130,7 @@ class CollaborationService { .map((b) => b.toString(16).padStart(2, '0')) .join('') .slice(0, 16); - return `storycraft-${hex}`; + return `worldscript-${hex}`; } // QNBS-v3: Serialize CollaborationUser to JSON, encrypt with AES-256-GCM, base64-encode for y-webrtc awareness transport. diff --git a/services/commands/palettePreferences.ts b/services/commands/palettePreferences.ts index b45e50188..f7a0e2dec 100644 --- a/services/commands/palettePreferences.ts +++ b/services/commands/palettePreferences.ts @@ -1,4 +1,4 @@ -const STORAGE_KEY = 'storycraft-palette-prefs-v1'; +const STORAGE_KEY = 'worldscript-palette-prefs-v1'; const MAX_RECENT = 15; const MAX_PINNED = 20; diff --git a/services/copilot/copilotContextService.ts b/services/copilot/copilotContextService.ts index 30ce0ff23..583d5bea6 100644 --- a/services/copilot/copilotContextService.ts +++ b/services/copilot/copilotContextService.ts @@ -88,8 +88,8 @@ export function buildSystemPrompt(ctx: CopilotContext): string { } return [ - 'You are the StoryCraft Studio Copilot — a warm, encouraging writing assistant for beginners.', - 'StoryCraft Studio is an offline-first creative-writing app. The AI is always called the "Co-Pilot".', + 'You are the WorldScript Studio Copilot — a warm, encouraging writing assistant for beginners.', + 'WorldScript Studio is an offline-first creative-writing app. The AI is always called the "Co-Pilot".', 'Keep answers short, concrete, and jargon-free. Prefer numbered steps for how-to questions.', 'When the user asks "what can I do here", explain the current screen specifically.', `The user is currently on the "${ctx.viewLabel}" screen, which is ${hint}.`, diff --git a/services/dbConstants.ts b/services/dbConstants.ts index d7ca9d2f8..a3dcee63e 100644 --- a/services/dbConstants.ts +++ b/services/dbConstants.ts @@ -1,9 +1,9 @@ /** Shared IndexedDB names and store identifiers — keep in sync with upgrade paths in `dbService.ts`. */ -export const LEGACY_DB_NAME = 'storycraft-db'; +export const LEGACY_DB_NAME = 'worldscript-db'; -export const STATE_DB_NAME = 'storycraft-state-db'; -export const DATA_DB_NAME = 'storycraft-data-db'; +export const STATE_DB_NAME = 'worldscript-state-db'; +export const DATA_DB_NAME = 'worldscript-data-db'; /** Current schema version for both state and data DBs. */ // QNBS-v3: v8 — projects-index-store for Cross-Project-Search v2; backwards-compatible addStore migration. @@ -21,4 +21,4 @@ export const CODEX_STORE = 'codex-store'; export const BINDER_ASSETS_STORE = 'binder-assets-store'; /** Written to `APP_DATA_STORE` after a successful legacy → dual-DB copy (idempotency). */ -export const LEGACY_DB_MIGRATION_MARKER_KEY = '__legacy_storycraft_db_migrated__'; +export const LEGACY_DB_MIGRATION_MARKER_KEY = '__legacy_worldscript_db_migrated__'; diff --git a/services/dbInitialization.ts b/services/dbInitialization.ts index ad014300d..1226bb552 100644 --- a/services/dbInitialization.ts +++ b/services/dbInitialization.ts @@ -8,7 +8,7 @@ import { logger } from './logger'; export interface InitStorageResult { success: boolean; - /** Whether a legacy storycraft-db migration was performed. */ + /** Whether a legacy worldscript-db migration was performed. */ migrated: boolean; /** Human-readable error message when success === false. */ error?: string; @@ -106,8 +106,8 @@ export async function resetAllDatabases(): Promise { await Promise.all([deleteIdb(STATE_DB_NAME), deleteIdb(DATA_DB_NAME)]); // Remove localStorage markers that guard migration idempotency + any plotBoard viewport state. - // QNBS-v3: Only storycraft-specific keys; avoids wiping unrelated site storage. - const prefix = ['storycraft', 'plotBoard', 'schemaVersion', '__legacy_storycraft']; + // QNBS-v3: Only worldscript-specific keys; avoids wiping unrelated site storage. + const prefix = ['worldscript', 'plotBoard', 'schemaVersion', '__legacy_worldscript']; for (let i = localStorage.length - 1; i >= 0; i--) { const key = localStorage.key(i); if (key && prefix.some((p) => key.startsWith(p))) { diff --git a/services/dbMigration.ts b/services/dbMigration.ts index 86f8f3a11..d49015cea 100644 --- a/services/dbMigration.ts +++ b/services/dbMigration.ts @@ -130,10 +130,10 @@ export type MigrateLegacyOptions = { }; /** - * One-time copy from pre–dual-DB `storycraft-db` into `storycraft-state-db` / `storycraft-data-db`. + * One-time copy from pre–dual-DB `worldscript-db` into `worldscript-state-db` / `worldscript-data-db`. * Idempotent: skips if marker exists, if state already holds project/settings, or if legacy is absent. */ -export async function migrateLegacyStorycraftDbIfNeeded( +export async function migrateLegacyWorldscriptDbIfNeeded( stateDb: IDBDatabase, dataDb: IDBDatabase, options?: MigrateLegacyOptions, @@ -141,7 +141,7 @@ export async function migrateLegacyStorycraftDbIfNeeded( const idb = options?.idb ?? globalThis.indexedDB; if (stateDb.name !== STATE_DB_NAME || dataDb.name !== DATA_DB_NAME) { - logger.warn('migrateLegacyStorycraftDbIfNeeded: unexpected DB names, skipping'); + logger.warn('migrateLegacyWorldscriptDbIfNeeded: unexpected DB names, skipping'); return { migrated: false, reason: 'invalid_target_dbs' }; } diff --git a/services/epubApiService.ts b/services/epubApiService.ts index 229365a42..bdefecdcd 100644 --- a/services/epubApiService.ts +++ b/services/epubApiService.ts @@ -217,7 +217,7 @@ ${ch.content?.trim() ? toParagraphs(ch.content) : '

(Emp ${lang} ${dateStr.slice(0, 10)} ${dateStr} - + ${manifest.join('\n ')} diff --git a/services/factoryResetService.ts b/services/factoryResetService.ts index b06951985..992dc8586 100644 --- a/services/factoryResetService.ts +++ b/services/factoryResetService.ts @@ -12,13 +12,13 @@ import { logger } from './logger'; /** All IDB databases the app may have created. */ const KNOWN_DB_NAMES = [ - 'storycraft-db', // legacy — migrated to storycraft-data-db - 'storycraft-state-db', - 'storycraft-data-db', - 'storycraft-logs-db', - 'storycraft-revisions-db', - 'storycraft-lora-db', - 'storycraft-inference-cache-db', + 'worldscript-db', // legacy — migrated to worldscript-data-db + 'worldscript-state-db', + 'worldscript-data-db', + 'worldscript-logs-db', + 'worldscript-revisions-db', + 'worldscript-lora-db', + 'worldscript-inference-cache-db', 'proforge-memory-bank', ]; diff --git a/services/fs/settingsFsStore.ts b/services/fs/settingsFsStore.ts index 8e96c1dc3..b5d58f2c7 100644 --- a/services/fs/settingsFsStore.ts +++ b/services/fs/settingsFsStore.ts @@ -95,7 +95,7 @@ export class FsSettingsStore extends FsCore { const encrypted = await encryptText( apiKey.trim(), - `${appDataPath}|${provider}|StoryCraftStudio|v1`, + `${appDataPath}|${provider}|WorldScriptStudio|v1`, ); const filePath = await apis.join(configPath, `${provider}_key.enc.json`); await retryFs(() => apis.writeTextFile(filePath, JSON.stringify(encrypted))); @@ -109,7 +109,7 @@ export class FsSettingsStore extends FsCore { if (!(await apis.exists(keyFile))) return null; const content = await retryFs(() => apis.readTextFile(keyFile)); const payload = JSON.parse(content) as { iv: string; data: string }; - return await decryptText(payload, `${appDataPath}|${provider}|StoryCraftStudio|v1`); + return await decryptText(payload, `${appDataPath}|${provider}|WorldScriptStudio|v1`); } catch (error) { logger.warn(`Failed to decrypt API key for provider "${provider}":`, error); return null; diff --git a/services/geminiService.ts b/services/geminiService.ts index 40e605307..a937d58d4 100644 --- a/services/geminiService.ts +++ b/services/geminiService.ts @@ -765,7 +765,7 @@ export const streamAiHelpResponse = async ( const ai = await getAiClient(); - const prompt = `You are a helpful assistant for a creative writing app called StoryCraft Studio. Answer the user's question concisely and clearly. Format your answer using Markdown.\n\n${sanitizePromptBlock(promptBody)}`; + const prompt = `You are a helpful assistant for a creative writing app called WorldScript Studio. Answer the user's question concisely and clearly. Format your answer using Markdown.\n\n${sanitizePromptBlock(promptBody)}`; const responseStream = await ai.models.generateContentStream({ model: getModelForText(), contents: prompt, diff --git a/services/libraryBackupService.ts b/services/libraryBackupService.ts index 7045dc678..88e03c9b0 100644 --- a/services/libraryBackupService.ts +++ b/services/libraryBackupService.ts @@ -7,7 +7,7 @@ import type { Settings, StoryProject } from '../types'; import type { BinderAssetPayload } from './storageBackend'; import { storageService } from './storageService'; -export const LIBRARY_BACKUP_FORMAT = 'storycraft-library-v1' as const; +export const LIBRARY_BACKUP_FORMAT = 'worldscript-library-v1' as const; // QNBS-v3: 600k matches OWASP 2024 minimum for PBKDF2-HMAC-SHA-256. const PBKDF2_ITERATIONS = 600_000; @@ -231,7 +231,7 @@ export async function decryptLibraryZipBlob( const metaFile = outer.file('META.json'); const vaultFile = outer.file('vault.bin'); if (!metaFile || !vaultFile) { - throw new Error('Invalid StoryCraft library backup: missing META.json or vault.bin'); + throw new Error('Invalid WorldScript library backup: missing META.json or vault.bin'); } const meta = JSON.parse(await metaFile.async('string')) as EncryptedLibraryZipMeta; const ciphertext = await vaultFile.async('uint8array'); diff --git a/services/localAiFacade.ts b/services/localAiFacade.ts index 3b96e06b8..a111a20d8 100644 --- a/services/localAiFacade.ts +++ b/services/localAiFacade.ts @@ -140,7 +140,7 @@ export async function generateLocalText( try { // QNBS-v3: When adaptive AI engine is enabled, use its task config for optimal backend/model. const adaptiveEnabled = - typeof window !== 'undefined' && window.__storycraft_adaptive_ai__ === true; + typeof window !== 'undefined' && window.__worldscript_adaptive_ai__ === true; // QNBS-v3: capture the typed adaptive config so recordTaskLatency keeps its ComputeBackend type. // Skipped entirely when the caller forces an exact model (explicit preload/download). diff --git a/services/logger.ts b/services/logger.ts index e916a551a..1de8c61ac 100644 --- a/services/logger.ts +++ b/services/logger.ts @@ -28,7 +28,7 @@ export function sanitizeLogContext(ctx: Record): Record { @@ -152,7 +152,7 @@ function writeToTauri(entry: LogEntry): void { function writeToConsole(entry: LogEntry): void { if (!isDev) return; - const tag = `[StoryCraft:${entry.level.toUpperCase()}:${entry.module}]`; + const tag = `[WorldScript:${entry.level.toUpperCase()}:${entry.module}]`; const ctx = entry.context ? ` ${JSON.stringify(entry.context)}` : ''; const msg = entry.message + ctx; switch (entry.level) { diff --git a/services/lora/loraOllamaService.ts b/services/lora/loraOllamaService.ts index 0ced98cba..2040daceb 100644 --- a/services/lora/loraOllamaService.ts +++ b/services/lora/loraOllamaService.ts @@ -136,6 +136,6 @@ export async function testOllamaAdapterPrompt( /** List only adapter-capable models (those tagged with LoRA style in metadata). */ export async function listOllamaAdapterModels(baseUrl?: string): Promise { const all = await listOllamaModels(baseUrl); - // Filter by name convention: storycraft-lora-* or any user-created lora models - return all.filter((m) => m.name.includes('lora') || m.name.startsWith('storycraft-')); + // Filter by name convention: worldscript-lora-* or any user-created lora models + return all.filter((m) => m.name.includes('lora') || m.name.startsWith('worldscript-')); } diff --git a/services/lora/loraTrainingService.ts b/services/lora/loraTrainingService.ts index fb9aa27d7..3dba10550 100644 --- a/services/lora/loraTrainingService.ts +++ b/services/lora/loraTrainingService.ts @@ -74,7 +74,7 @@ export async function startTraining( onProgress: (event: TrainingProgressEvent) => void, ): Promise { if (!isTauri()) { - throw new Error('LoRA training requires the StoryCraft Studio desktop app.'); + throw new Error('LoRA training requires the WorldScript Studio desktop app.'); } const runId = uuid(); logger.info('loraTrainingService: starting training run', { runId, preset: config.preset.id }); diff --git a/services/loraAdapterService.ts b/services/loraAdapterService.ts index 8cd162587..54b5218e1 100644 --- a/services/loraAdapterService.ts +++ b/services/loraAdapterService.ts @@ -26,7 +26,7 @@ export interface LoraAdapterMeta { localPath?: string; } -const DB_NAME = 'storycraft-lora-db'; +const DB_NAME = 'worldscript-lora-db'; // QNBS-v3: v2 — adds lora-datasets, lora-runs, lora-active stores. const DB_VERSION = 2; const META_STORE = 'lora-meta'; diff --git a/services/pluginRegistry.ts b/services/pluginRegistry.ts index 4fee4cac5..cb9a84c7e 100644 --- a/services/pluginRegistry.ts +++ b/services/pluginRegistry.ts @@ -1,5 +1,5 @@ /** - * Plugin registry — lightweight service for discovering and managing StoryCraft Studio extensions. + * Plugin registry — lightweight service for discovering and managing WorldScript Studio extensions. * QNBS-v3: Plugins declare a capability manifest; execute() validates permissions before dispatch. * v2: Worker-scope isolation via routeTask to plugin.worker.ts (P0-2). * Telemetry: All plugin executions are logged to the structured logger for observability. diff --git a/services/proForge/adapters/nodeInferenceGateway.ts b/services/proForge/adapters/nodeInferenceGateway.ts index c15f8bc1e..49b1f4e86 100644 --- a/services/proForge/adapters/nodeInferenceGateway.ts +++ b/services/proForge/adapters/nodeInferenceGateway.ts @@ -33,10 +33,10 @@ export interface NodeGatewayOptions { /** Resolve the API key from common env vars; throws a clear error when missing. */ export function resolveNodeApiKey(env: NodeJS.ProcessEnv = process.env): string { const key = - env['GEMINI_API_KEY'] ?? env['STORYCRAFT_API_KEY'] ?? env['GOOGLE_GENERATIVE_AI_API_KEY']; + env['GEMINI_API_KEY'] ?? env['WORLDSCRIPT_API_KEY'] ?? env['GOOGLE_GENERATIVE_AI_API_KEY']; if (!key) { throw new Error( - 'Missing API key. Set GEMINI_API_KEY (or STORYCRAFT_API_KEY) to enable AI-backed ProForge stages.', + 'Missing API key. Set GEMINI_API_KEY (or WORLDSCRIPT_API_KEY) to enable AI-backed ProForge stages.', ); } return key; diff --git a/services/projectImportSchema.ts b/services/projectImportSchema.ts index 0221076ea..32135601a 100644 --- a/services/projectImportSchema.ts +++ b/services/projectImportSchema.ts @@ -1,5 +1,5 @@ /** - * Shared Zod validation for StoryCraft project JSON imports (browser + Tauri). + * Shared Zod validation for WorldScript project JSON imports (browser + Tauri). * QNBS-v3: Einheitliches Schema verhindert divergierende Parse-Pfade und härter gegen korrupte Dateien. */ import { z } from 'zod'; diff --git a/services/promptLibrary.ts b/services/promptLibrary.ts index 3f21e3817..332614e5d 100644 --- a/services/promptLibrary.ts +++ b/services/promptLibrary.ts @@ -1,5 +1,5 @@ /** - * Centralised prompt template registry for StoryCraft Studio. + * Centralised prompt template registry for WorldScript Studio. * QNBS-v3: Single source of truth for all AI prompts — enables versioning, A/B testing, * export/import, and future locale-aware overrides without touching geminiService. */ @@ -318,7 +318,7 @@ register({ category: 'proforge-diagnostic', localeKey: 'promptLibrary.diagnosticReport', template: (v) => - `You are the StoryCraft ProForge Diagnostic Agent. Analyze this manuscript comprehensively.\n\nTITLE: ${v['title'] ?? ''}\nLOGLINE: ${v['logline'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\nWORD COUNT: ${v['wordCount'] ?? '0'}\nSECTIONS: ${v['sectionCount'] ?? '0'}\nAVG SECTION LENGTH: ${v['avgSectionLength'] ?? '0'}\n\nOUTLINE:\n${v['outline'] ?? ''}\n\nMANUSCRIPT EXCERPT:\n${v['manuscriptExcerpt'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return a JSON object conforming to the DiagnosticReport schema with: profile, consistencyIssues (max 20), structuralGaps (max 15), qualityScore (overall 0-100 with sub-scores), recommendedConfig, and summary (max 500 words).`, + `You are the WorldScript ProForge Diagnostic Agent. Analyze this manuscript comprehensively.\n\nTITLE: ${v['title'] ?? ''}\nLOGLINE: ${v['logline'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\nWORD COUNT: ${v['wordCount'] ?? '0'}\nSECTIONS: ${v['sectionCount'] ?? '0'}\nAVG SECTION LENGTH: ${v['avgSectionLength'] ?? '0'}\n\nOUTLINE:\n${v['outline'] ?? ''}\n\nMANUSCRIPT EXCERPT:\n${v['manuscriptExcerpt'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return a JSON object conforming to the DiagnosticReport schema with: profile, consistencyIssues (max 20), structuralGaps (max 15), qualityScore (overall 0-100 with sub-scores), recommendedConfig, and summary (max 500 words).`, }); register({ @@ -328,7 +328,7 @@ register({ category: 'proforge-structural', localeKey: 'promptLibrary.structuralEditPlan', template: (v) => - `You are the StoryCraft ProForge Structural Agent. Analyze macro-structure, pacing, and arcs.\n\nTITLE: ${v['title'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nSECTION LIST:\n${v['sectionList'] ?? ''}\n\nMANUSCRIPT EXCERPT:\n${v['manuscriptExcerpt'] ?? ''}\n\nPRIOR DIAGNOSTIC:\n${v['diagnosticSummary'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: edits array (max 50, each with id, sectionId, category, original, proposed, rationale, confidence 0-1), pacingReport (sectionPacing with tensionScore 0-10, recommendedAction), overallPacing, suggestions, and summary.`, + `You are the WorldScript ProForge Structural Agent. Analyze macro-structure, pacing, and arcs.\n\nTITLE: ${v['title'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nSECTION LIST:\n${v['sectionList'] ?? ''}\n\nMANUSCRIPT EXCERPT:\n${v['manuscriptExcerpt'] ?? ''}\n\nPRIOR DIAGNOSTIC:\n${v['diagnosticSummary'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: edits array (max 50, each with id, sectionId, category, original, proposed, rationale, confidence 0-1), pacingReport (sectionPacing with tensionScore 0-10, recommendedAction), overallPacing, suggestions, and summary.`, }); register({ @@ -338,7 +338,7 @@ register({ category: 'proforge-prose', localeKey: 'promptLibrary.proseEditBatch', template: (v) => - `You are the StoryCraft ProForge Prose Agent. Perform line-level editing for: show-don't-tell, filter words, dialogue tags, POV consistency, sensory details, weak verbs, adverbs.\n\nSECTION: ${v['sectionTitle'] ?? ''}\nWORD COUNT: ${v['wordCount'] ?? '0'}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nCONTENT:\n${v['sectionContent'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: edits array (max 30 per section, each with id, sectionId, startOffset, endOffset, category, original, proposed, rationale, confidence), beforeMetrics (adverbDensity, filterWordDensity, dialogueRatio, sensoryScore, showDontTellScore, povConsistencyScore), and summary.`, + `You are the WorldScript ProForge Prose Agent. Perform line-level editing for: show-don't-tell, filter words, dialogue tags, POV consistency, sensory details, weak verbs, adverbs.\n\nSECTION: ${v['sectionTitle'] ?? ''}\nWORD COUNT: ${v['wordCount'] ?? '0'}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nCONTENT:\n${v['sectionContent'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: edits array (max 30 per section, each with id, sectionId, startOffset, endOffset, category, original, proposed, rationale, confidence), beforeMetrics (adverbDensity, filterWordDensity, dialogueRatio, sensoryScore, showDontTellScore, povConsistencyScore), and summary.`, }); register({ @@ -348,7 +348,7 @@ register({ category: 'proforge-copyedit', localeKey: 'promptLibrary.copyEditPlan', template: (v) => - `You are the StoryCraft ProForge Copy Editor. Fix grammar, spelling, punctuation, style consistency, repetition, and formatting.\n\nSECTION: ${v['sectionTitle'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nSTYLE GUIDE: ${v['styleGuide'] ?? 'Standard'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nCONTENT:\n${v['sectionContent'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: grammarEdits (max 20, with ruleId, ruleName, original, proposed, explanation), styleEdits (max 15, category: register/tone/formality/redundancy), repetitionHits (max 10, wordOrPhrase, occurrences, count), formatIssues (max 10), and summary.`, + `You are the WorldScript ProForge Copy Editor. Fix grammar, spelling, punctuation, style consistency, repetition, and formatting.\n\nSECTION: ${v['sectionTitle'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nSTYLE GUIDE: ${v['styleGuide'] ?? 'Standard'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nCONTENT:\n${v['sectionContent'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: grammarEdits (max 20, with ruleId, ruleName, original, proposed, explanation), styleEdits (max 15, category: register/tone/formality/redundancy), repetitionHits (max 10, wordOrPhrase, occurrences, count), formatIssues (max 10), and summary.`, }); register({ @@ -358,7 +358,7 @@ register({ category: 'proforge-proof', localeKey: 'promptLibrary.qualityGateReport', template: (v) => - `You are the StoryCraft ProForge Quality Gate Agent. Perform final proofreading, technical validation, legal scan, and readability assessment.\n\nTITLE: ${v['title'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nMANUSCRIPT:\n${v['manuscript'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: overallPass (boolean), grammar {pass, score, issues}, style {pass, score, issues}, technical {pass, score, issues}, legal {pass, score, warnings (type: trademark/realPerson/sensitiveContent/copyright/defamation, severity: critical/warning)}, readability {pass, score, metrics (fleschKincaid, fleschReadingEase, targetAgeMin, targetAgeMax, appropriateForGenre)}, and summary.`, + `You are the WorldScript ProForge Quality Gate Agent. Perform final proofreading, technical validation, legal scan, and readability assessment.\n\nTITLE: ${v['title'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\n\nMANUSCRIPT:\n${v['manuscript'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: overallPass (boolean), grammar {pass, score, issues}, style {pass, score, issues}, technical {pass, score, issues}, legal {pass, score, warnings (type: trademark/realPerson/sensitiveContent/copyright/defamation, severity: critical/warning)}, readability {pass, score, metrics (fleschKincaid, fleschReadingEase, targetAgeMin, targetAgeMax, appropriateForGenre)}, and summary.`, }); register({ @@ -368,7 +368,7 @@ register({ category: 'proforge-publishing', localeKey: 'promptLibrary.publishingPackage', template: (v) => - `You are the StoryCraft ProForge Publishing Agent. Generate all publishing metadata and marketing assets.\n\nTITLE: ${v['title'] ?? ''}\nLOGLINE: ${v['logline'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\nWORD COUNT: ${v['wordCount'] ?? '0'}\n\nEXCERPT:\n${v['excerpt'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: metadata (title, subtitle, author, description, keywords[10], genre, bisacCodes[5], language, pageCount, wordCount), blurbs (backCover, amazonDescription, tagline, elevatorPitch, socialMediaPosts[5]), audiobookGuide (chapterMarks with estimatedDurationMinutes, narratorNotes, pronunciationNotes), marketingAssets (socialMediaPosts, newsletterText, adCopyVariants[5], authorBioSuggestion), and rightsPage.`, + `You are the WorldScript ProForge Publishing Agent. Generate all publishing metadata and marketing assets.\n\nTITLE: ${v['title'] ?? ''}\nLOGLINE: ${v['logline'] ?? ''}\nGENRE: ${v['genre'] ?? 'general-fiction'}\nLANGUAGE: ${v['language'] ?? 'English'}\nWORD COUNT: ${v['wordCount'] ?? '0'}\n\nEXCERPT:\n${v['excerpt'] ?? ''}\n\n${v['memoryContext'] ? `MEMORY CONTEXT:\n${v['memoryContext']}\n\n` : ''}Return JSON with: metadata (title, subtitle, author, description, keywords[10], genre, bisacCodes[5], language, pageCount, wordCount), blurbs (backCover, amazonDescription, tagline, elevatorPitch, socialMediaPosts[5]), audiobookGuide (chapterMarks with estimatedDurationMinutes, narratorNotes, pronunciationNotes), marketingAssets (socialMediaPosts, newsletterText, adCopyVariants[5], authorBioSuggestion), and rightsPage.`, }); // --------------------------------------------------------------------------- diff --git a/services/sceneRevisionService.ts b/services/sceneRevisionService.ts index b841b8a61..89f7dafbd 100644 --- a/services/sceneRevisionService.ts +++ b/services/sceneRevisionService.ts @@ -2,7 +2,7 @@ // Max 50 revisions per scene; oldest are evicted automatically on save. import type { SceneRevision } from '../types'; -const DB_NAME = 'storycraft-revisions-db'; +const DB_NAME = 'worldscript-revisions-db'; const DB_VERSION = 1; const STORE = 'scene-revisions'; const MAX_PER_SCENE = 50; diff --git a/services/settingsExchange.ts b/services/settingsExchange.ts index 3b41be6ad..4e57463cd 100644 --- a/services/settingsExchange.ts +++ b/services/settingsExchange.ts @@ -4,7 +4,7 @@ import type { Settings } from '../types'; export const SETTINGS_EXPORT_VERSION = 1 as const; export const settingsExportEnvelopeSchema = z.object({ - storycraftSettingsExportVersion: z.literal(SETTINGS_EXPORT_VERSION), + worldscriptSettingsExportVersion: z.literal(SETTINGS_EXPORT_VERSION), settings: z.record(z.string(), z.unknown()), }); @@ -12,7 +12,7 @@ export type SettingsExportEnvelope = z.infer) }, }; } diff --git a/services/spotlightTour.ts b/services/spotlightTour.ts index eb0ad80ae..df303e7eb 100644 --- a/services/spotlightTour.ts +++ b/services/spotlightTour.ts @@ -3,7 +3,7 @@ import { driver } from 'driver.js'; import 'driver.js/dist/driver.css'; /** Persisted when the user finishes or closes the spotlight tour. */ -export const SPOTLIGHT_TOUR_STORAGE_KEY = 'storycraft-spotlight-tour-done'; +export const SPOTLIGHT_TOUR_STORAGE_KEY = 'worldscript-spotlight-tour-done'; export function markSpotlightTourComplete(): void { try { @@ -110,7 +110,7 @@ export function startSpotlightTour(t: Translate, tourId: SpotlightTourId = 'defa nextBtnText: t('tour.btn.next'), prevBtnText: t('tour.btn.prev'), doneBtnText: t('tour.btn.done'), - popoverClass: 'storycraft-driver-popover', + popoverClass: 'worldscript-driver-popover', steps, onDestroyed: () => { markSpotlightTourComplete(); diff --git a/services/storage/idbCore.ts b/services/storage/idbCore.ts index 137916b95..91a4efd09 100644 --- a/services/storage/idbCore.ts +++ b/services/storage/idbCore.ts @@ -17,7 +17,7 @@ import { SNAPSHOTS_STORE, STATE_DB_NAME, } from '../dbConstants'; -import { migrateLegacyStorycraftDbIfNeeded } from '../dbMigration'; +import { migrateLegacyWorldscriptDbIfNeeded } from '../dbMigration'; import { logger } from '../logger'; // LZ-String threshold: compress payloads >10 KB @@ -177,9 +177,9 @@ export class IdbConnectionManager { await Promise.all([this.openStateDb(), this.openDataDb()]); if (this.stateDb && this.dataDb) { try { - const result = await migrateLegacyStorycraftDbIfNeeded(this.stateDb, this.dataDb); + const result = await migrateLegacyWorldscriptDbIfNeeded(this.stateDb, this.dataDb); if (result.migrated) { - logger.info('Migrated legacy IndexedDB (storycraft-db) to dual-database layout.'); + logger.info('Migrated legacy IndexedDB (worldscript-db) to dual-database layout.'); } } catch (error) { logger.warn('Legacy IndexedDB migration step failed:', error); diff --git a/services/storage/idbKeyStore.ts b/services/storage/idbKeyStore.ts index 46618e492..f6b273bc6 100644 --- a/services/storage/idbKeyStore.ts +++ b/services/storage/idbKeyStore.ts @@ -16,7 +16,7 @@ export class IdbKeyStore extends IdbConnectionManager { /** Legacy key derivation — used only for migrating existing encrypted data. */ private async getLegacyCryptoKey(): Promise { const material = new TextEncoder().encode( - `${location.origin}|StoryCraftStudio|gemini-key-v1|${navigator.userAgent.slice(0, 50)}`, + `${location.origin}|WorldScriptStudio|gemini-key-v1|${navigator.userAgent.slice(0, 50)}`, ); const hash = await crypto.subtle.digest('SHA-256', material); return crypto.subtle.importKey('raw', hash, { name: 'AES-GCM' }, false, ['encrypt', 'decrypt']); diff --git a/services/storage/idbProjectStore.ts b/services/storage/idbProjectStore.ts index e57def615..ebffa5076 100644 --- a/services/storage/idbProjectStore.ts +++ b/services/storage/idbProjectStore.ts @@ -106,7 +106,7 @@ export function normalizePersistedSettings(incoming: Record): S localBackendPreset: 'ollama_default', openAiCompatibleBaseUrl: '', openAiSiteUrl: '', - openAiSiteTitle: 'StoryCraft Studio', + openAiSiteTitle: 'WorldScript Studio', hybridFallbackEnabled: false, hybridFallbackChain: [], ragMode: 'hybrid', diff --git a/services/storage/storageEncryptionService.ts b/services/storage/storageEncryptionService.ts index a16b0c0ca..a70eb1ae0 100644 --- a/services/storage/storageEncryptionService.ts +++ b/services/storage/storageEncryptionService.ts @@ -21,7 +21,7 @@ import { const PBKDF2_ITERATIONS = 600_000; // OWASP 2024 minimum for PBKDF2-HMAC-SHA-256 const IV_BYTE_LENGTH = 12; const SALT_BYTE_LENGTH = 32; -const SALT_STORAGE_KEY = 'storycraft-idb-kdf-salt-v1'; +const SALT_STORAGE_KEY = 'worldscript-idb-kdf-salt-v1'; // \x00enc1\x00 — 6-byte sentinel distinct from LZ prefix \x00lz1\x00 const SENTINEL = new Uint8Array([0x00, 0x65, 0x6e, 0x63, 0x31, 0x00]); diff --git a/services/tauriDeepLink.ts b/services/tauriDeepLink.ts index 88eaa56d4..c0f1de5f6 100644 --- a/services/tauriDeepLink.ts +++ b/services/tauriDeepLink.ts @@ -1,7 +1,7 @@ /** * Tauri Deep Link Service — handles native file associations and single-instance behavior. * QNBS-v3: Listens for `deep-link://new-url` events emitted from the deep-link plugin - * when a .storycraft or .scst file is double-clicked or dragged onto the app icon. + * when a .worldscript or .wsst file is double-clicked or dragged onto the app icon. * Integrates with the existing importProjectThunk flow for seamless project loading. */ @@ -48,21 +48,21 @@ export async function initTauriDeepLink( log.info('Received deep-link event', { url }); - // Parse the URL to get the file path (storycraft://path/to/file.storycraft) + // Parse the URL to get the file path (worldscript://path/to/file.worldscript) // The deep-link plugin handles custom schemes, but for file associations we need // to handle the case where the file path is passed as a CLI argument try { // For file associations on Windows/Linux, the deep-link plugin parses CLI args - // and emits the URL. We need to convert storycraft:// URLs back to file paths + // and emits the URL. We need to convert worldscript:// URLs back to file paths // or handle direct file paths if passed. let filePath = url; - // Check if it's a storycraft:// URL and extract the path - if (url.startsWith('storycraft://') || url.startsWith('storycraft:')) { - // On Windows, the URL might be storycraft:///C:/path/to/file.storycraft - // On Linux, it might be storycraft:///home/user/file.storycraft - // QNBS-v3: Strip storycraft:// prefix and normalize Windows drive-letter paths - filePath = url.replace(/^storycraft:\/\/?/, ''); + // Check if it's a worldscript:// URL and extract the path + if (url.startsWith('worldscript://') || url.startsWith('worldscript:')) { + // On Windows, the URL might be worldscript:///C:/path/to/file.worldscript + // On Linux, it might be worldscript:///home/user/file.worldscript + // QNBS-v3: Strip worldscript:// prefix and normalize Windows drive-letter paths + filePath = url.replace(/^worldscript:\/\/?/, ''); // Windows paths like /C:/... need the leading slash removed if (/^[A-Za-z]:/.test(filePath)) { filePath = filePath.replace(/^\/+/, ''); @@ -127,11 +127,11 @@ export async function initTauriDeepLink( } /** - * Check if a file path is a StoryCraft project file. + * Check if a file path is a WorldScript project file. */ -export function isStoryCraftProjectFile(filePath: string): boolean { +export function isWorldScriptProjectFile(filePath: string): boolean { const ext = filePath.split('.').pop()?.toLowerCase(); - return ext === 'storycraft' || ext === 'scst' || filePath.endsWith('.json'); + return ext === 'worldscript' || ext === 'wsst' || filePath.endsWith('.json'); } /** @@ -140,5 +140,5 @@ export function isStoryCraftProjectFile(filePath: string): boolean { export function getProjectIdFromPath(filePath: string): string { const lastSegment = filePath.split(/[/\\]/).pop(); if (!lastSegment) return 'unknown'; - return lastSegment.replace(/\.(storycraft|scst|json)$/, '') || 'unknown'; + return lastSegment.replace(/\.(worldscript|wsst|json)$/, '') || 'unknown'; } diff --git a/services/tauriTaskBridge.ts b/services/tauriTaskBridge.ts index e97b38e20..29bcf4699 100644 --- a/services/tauriTaskBridge.ts +++ b/services/tauriTaskBridge.ts @@ -1,5 +1,5 @@ // QNBS-v3: Phase 2 — Tauri bridge for Rust TaskSupervisor. Requires Tauri 2 + the Rust command -// `storycraft_task_supervisor_submit` and `storycraft_task_supervisor_ping` registered in +// `worldscript_task_supervisor_submit` and `worldscript_task_supervisor_ping` registered in // src-tauri/src/commands/task_supervisor.rs. Gracefully unavailable in web-browser context. import type { RustTaskRequest, RustTaskResultEvent } from '@domain/worker-bus'; @@ -18,7 +18,7 @@ export async function invokeRustTask(request: RustTaskRequest): Promise('storycraft_task_supervisor_submit', { + const result = await invoke('worldscript_task_supervisor_submit', { request, }); log.info('Rust TaskSupervisor completed task', { @@ -48,7 +48,7 @@ export async function isRustComputeAvailable(): Promise { } try { const { invoke } = await import('@tauri-apps/api/core'); - await invoke('storycraft_task_supervisor_ping'); + await invoke('worldscript_task_supervisor_ping'); _rustAvailableCache = { value: true, checkedAt: now }; return true; } catch { diff --git a/services/voice/voiceCommandService.ts b/services/voice/voiceCommandService.ts index b311d1cd9..c2048473e 100644 --- a/services/voice/voiceCommandService.ts +++ b/services/voice/voiceCommandService.ts @@ -88,7 +88,7 @@ export class VoiceCommandService { speechVolume: 1.0, allowCloudFallback: false, listeningTimeoutSeconds: 8, - wakeWordPhrase: 'Hey StoryCraft', + wakeWordPhrase: 'Hey WorldScript', ttsMuted: false, dictationAutoPunctuation: true, enableVoiceWasm: false, diff --git a/services/voice/wakeWordEngine.ts b/services/voice/wakeWordEngine.ts index fb3ca0d0b..cb25b9fe2 100644 --- a/services/voice/wakeWordEngine.ts +++ b/services/voice/wakeWordEngine.ts @@ -11,7 +11,7 @@ import type { AudioChunk, WakeWordEngine } from './voiceTypes'; export class EnergyThresholdWakeWordEngine implements WakeWordEngine { readonly name = 'Energy Threshold Wake-Word'; - private phrase = 'hey storycraft'; + private phrase = 'hey worldscript'; private recentTranscripts: string[] = []; private maxRecent = 5; diff --git a/src-tauri/src/commands/task_supervisor.rs b/src-tauri/src/commands/task_supervisor.rs index b74c8ee3f..9dfb2b797 100644 --- a/src-tauri/src/commands/task_supervisor.rs +++ b/src-tauri/src/commands/task_supervisor.rs @@ -2,7 +2,7 @@ //! //! QNBS-v3: Native compute backend for the hybrid router. The TS side //! (`services/tauriTaskBridge.ts` + `services/hybridRouter.ts`) already routes to -//! `storycraft_task_supervisor_submit` / `storycraft_task_supervisor_ping` when +//! `worldscript_task_supervisor_submit` / `worldscript_task_supervisor_ping` when //! `enableRustCompute` is on and a Tauri runtime is detected. This module supplies //! the missing native half: a deterministic, dependency-light task dispatcher plus //! one real CPU-bound task (`text.analyze`) that is genuinely worth offloading the @@ -71,7 +71,7 @@ pub struct TextAnalysis { /// Health-check command. Any non-error response signals the supervisor is reachable; /// the TS bridge treats a successful resolve as "Rust compute available". #[tauri::command] -pub fn storycraft_task_supervisor_ping() -> Result { +pub fn worldscript_task_supervisor_ping() -> Result { Ok(SUPERVISOR_VERSION.to_string()) } @@ -81,7 +81,7 @@ pub fn storycraft_task_supervisor_ping() -> Result { /// tasks; reserves `Err` for transport-level problems (none currently). This keeps /// the router's fallback logic driven by `result.success`, not by a thrown error. #[tauri::command] -pub fn storycraft_task_supervisor_submit(request: RustTaskRequest) -> Result { +pub fn worldscript_task_supervisor_submit(request: RustTaskRequest) -> Result { let started = Instant::now(); let task_id = request.task_id.clone(); @@ -266,7 +266,7 @@ mod tests { timeout_ms: 1000, retry_policy: None, }; - let res = storycraft_task_supervisor_submit(req).unwrap(); + let res = worldscript_task_supervisor_submit(req).unwrap(); assert!(!res.success); assert!(res.error.is_some()); assert_eq!(res.payload, Value::Null); @@ -283,7 +283,7 @@ mod tests { timeout_ms: 1000, retry_policy: None, }; - let res = storycraft_task_supervisor_submit(req).unwrap(); + let res = worldscript_task_supervisor_submit(req).unwrap(); assert!(res.success); assert!(res.error.is_none()); assert_eq!(res.payload["wordCount"], json!(5)); @@ -301,7 +301,7 @@ mod tests { timeout_ms: 1000, retry_policy: None, }; - let res = storycraft_task_supervisor_submit(req).unwrap(); + let res = worldscript_task_supervisor_submit(req).unwrap(); assert!(!res.success); assert!(res.error.unwrap().contains("payload.text")); } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 91746d7fd..319e265b8 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -60,8 +60,8 @@ pub fn run() { lora::abort_lora_training, lora::generate_ollama_modelfile, lora::check_lora_environment, - commands::task_supervisor::storycraft_task_supervisor_ping, - commands::task_supervisor::storycraft_task_supervisor_submit, + commands::task_supervisor::worldscript_task_supervisor_ping, + commands::task_supervisor::worldscript_task_supervisor_submit, ]) .setup(|app| { if cfg!(debug_assertions) { diff --git a/src-tauri/src/pandoc.rs b/src-tauri/src/pandoc.rs index 8757acf84..e61b1f923 100644 --- a/src-tauri/src/pandoc.rs +++ b/src-tauri/src/pandoc.rs @@ -19,7 +19,7 @@ pub fn pandoc_markdown_to_epub(markdown: String) -> Result = ({ position = ' setOpen(false)} - title="StoryCraft Drawer" + title="WorldScript Drawer" position={position} >

diff --git a/stories/Modal.stories.tsx b/stories/Modal.stories.tsx index 5bff31b7a..0772c76df 100644 --- a/stories/Modal.stories.tsx +++ b/stories/Modal.stories.tsx @@ -66,7 +66,7 @@ const ModalExample: React.FC<{ size: 'default' | 'lg' | 'xl' | undefined }> = ({ setIsOpen(false)} - title="StoryCraft Modal" + title="WorldScript Modal" size={size} >
diff --git a/stories/PWAComponents.stories.tsx b/stories/PWAComponents.stories.tsx index e9a92ddb4..e394109d8 100644 --- a/stories/PWAComponents.stories.tsx +++ b/stories/PWAComponents.stories.tsx @@ -40,7 +40,7 @@ const InstallBanner = ({ className="fixed bottom-20 left-1/2 -translate-x-1/2 w-[calc(100%-2rem)] max-w-sm z-50 rounded-xl bg-[var(--sc-surface-raised)] border border-[var(--sc-border-subtle)] p-4 shadow-2xl" >

- Install StoryCraft Studio + Install WorldScript Studio