(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