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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -460,10 +460,10 @@ jobs:
env:
CI: true
run: |
# QNBS-v3: The GitHub Pages build has base=/StoryCraft-Studio/. Mirror that structure
# QNBS-v3: The GitHub Pages build has base=/WorldScript-Studio/. Mirror that structure
# so http-server serves assets at the expected paths matching playwright.config baseURL.
mkdir -p serve_root/StoryCraft-Studio
cp -r dist/. serve_root/StoryCraft-Studio/
mkdir -p serve_root/WorldScript-Studio
cp -r dist/. serve_root/WorldScript-Studio/
pnpm exec http-server serve_root -p 3000 -s &
pnpm exec wait-on http://127.0.0.1:3000 --timeout 30000
pnpm run test:vrt
Expand Down
16 changes: 15 additions & 1 deletion .lighthouserc.cjs
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
// QNBS-v3: derive the audited URL from the same deploy-base logic the build/preview use
// (mirror of scripts/resolve-deploy-base.mjs — keep in sync). Hardcoding the GitHub Pages
// base made LHCI audit a 404 when the preview was built with VITE_BASE=/ or DEPLOY_TARGET=edge.
function resolveDeployBase() {
const viteBase = process.env.VITE_BASE?.trim();
if (viteBase) {
return viteBase.endsWith('/') ? viteBase : `${viteBase}/`;
}
if (process.env.DEPLOY_TARGET === 'edge') {
return '/';
}
return '/WorldScript-Studio/';
}

module.exports = {
ci: {
collect: {
startServerCommand: 'pnpm run preview',
startServerReadyPattern: 'Local',
url: ['http://127.0.0.1:4173/StoryCraft-Studio/'],
url: [`http://127.0.0.1:4173${resolveDeployBase()}`],
numberOfRuns: 3,
settings: {
// QNBS-v3: harden Chrome launch on CI runners. `--disable-dev-shm-usage` is the fix for the
Expand Down
16 changes: 15 additions & 1 deletion .lighthouserc.desktop.cjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
// QNBS-v3: Desktop Lighthouse config — runs alongside the mobile config (.lighthouserc.cjs).
// Serves production dist (pnpm run preview) and audits with desktop emulation.
// continue-on-error: true in ci.yml until desktop baselines stabilise.
// QNBS-v3: derive the audited URL from the same deploy-base logic the build/preview use
// (mirror of scripts/resolve-deploy-base.mjs — keep in sync). Hardcoding the GitHub Pages
// base made LHCI audit a 404 when the preview was built with VITE_BASE=/ or DEPLOY_TARGET=edge.
function resolveDeployBase() {
const viteBase = process.env.VITE_BASE?.trim();
if (viteBase) {
return viteBase.endsWith('/') ? viteBase : `${viteBase}/`;
}
if (process.env.DEPLOY_TARGET === 'edge') {
return '/';
}
return '/WorldScript-Studio/';
}

module.exports = {
ci: {
collect: {
startServerCommand: 'pnpm run preview',
startServerReadyPattern: 'Local',
url: ['http://127.0.0.1:4173/StoryCraft-Studio/'],
url: [`http://127.0.0.1:4173${resolveDeployBase()}`],
numberOfRuns: 2,
settings: {
// QNBS-v3: same /dev/shm crash hardening as the mobile config (.lighthouserc.cjs) —
Expand Down
4 changes: 2 additions & 2 deletions .mcp/proforge-mcp-server/package.json
Original file line number Diff line number Diff line change
@@ -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"
},
Expand Down
24 changes: 12 additions & 12 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />

<!-- ── Manifest & Icons ───────────────────────────────── -->
<!-- QNBS-v3: %BASE_URL% substituted by Vite at build time — resolves to / for Vercel/CF edge and /StoryCraft-Studio/ for GitHub Pages. -->
<!-- QNBS-v3: %BASE_URL% substituted by Vite at build time — resolves to / for Vercel/CF edge and /WorldScript-Studio/ for GitHub Pages. -->
<link rel="manifest" href="%BASE_URL%manifest.json" />
<link rel="icon" type="image/svg+xml" href="%BASE_URL%favicon.svg" />
<link rel="apple-touch-icon" href="%BASE_URL%favicon.svg" />
Expand All @@ -18,31 +18,31 @@
<!-- ── SEO & Social ───────────────────────────────────── -->
<meta name="description" content="AI-Powered Creative Writing Studio — craft entire novels, characters & worlds with Gemini AI. Works offline." />
<meta name="keywords" content="creative writing, AI writing assistant, novel writer, story generator, character builder, world building" />
<meta name="author" content="StoryCraft Studio" />
<meta name="author" content="WorldScript Studio" />
<meta name="robots" content="index, follow" />

<!-- Open Graph -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="StoryCraft Studio" />
<meta property="og:title" content="StoryCraft Studio — AI Creative Writing" />
<meta property="og:description" content="Craft entire novels, characters & worlds with Gemini AI. Works offline as a PWA." />
<meta property="og:url" content="https://storycraft-studio.app/StoryCraft-Studio/" />
<meta property="og:site_name" content="WorldScript Studio" />
<meta property="og:title" content="WorldScript Studio — AI Creative Writing" />
<meta property="og:description" content="Build immersive worlds and craft entire novels with AI. Works offline as a PWA." />
<meta property="og:url" content="https://worldscript-studio.app%BASE_URL%" />
<meta property="og:image" content="%BASE_URL%favicon.svg" />

<!-- Twitter Card -->
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="StoryCraft Studio" />
<meta name="twitter:description" content="AI-Powered Creative Writing Studio — Gemini AI, offline-ready PWA." />
<meta name="twitter:title" content="WorldScript Studio" />
<meta name="twitter:description" content="AI-Powered Creative Writing Studio — offline-ready PWA." />
<meta name="twitter:image" content="%BASE_URL%favicon.svg" />

<!-- ── PWA / Mobile Capable ──────────────────────────── -->
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="StoryCraft" />
<meta name="apple-mobile-web-app-title" content="WorldScript" />
<meta name="msapplication-TileColor" content="#020617" />
<meta name="msapplication-tap-highlight" content="no" />
<meta name="application-name" content="StoryCraft Studio" />
<meta name="application-name" content="WorldScript Studio" />

<!-- ── Security: Content Security Policy ─────────────── -->
<!--
Expand All @@ -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,
Expand Down Expand Up @@ -91,7 +91,7 @@
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+JP:wght@400;500;700&family=Noto+Sans+GR:wght@400;500;700&family=Noto+Sans+Arabic:wght@400;500;700&family=Noto+Sans+Hebrew:wght@400;500;700&display=swap" rel="stylesheet">

<title>StoryCraft Studio</title>
<title>WorldScript Studio</title>
</head>
<body>
<!-- QNBS-v3: Disable Aurora on low-end hardware (≤4 logical CPUs) to prevent GPU/battery drain; prefers-reduced-data handled in CSS. -->
Expand Down
14 changes: 12 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion packages/collab-transport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
2 changes: 1 addition & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Comment thread
qnbs marked this conversation as resolved.
trace: 'on-first-retry',
screenshot: 'only-on-failure',
// QNBS-v3: action + navigation timeouts bound to detect UI hangs / stalled
Expand Down
6 changes: 3 additions & 3 deletions public/404.html
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>StoryCraft Studio</title>
<title>WorldScript Studio</title>
<script>
// GitHub Pages Redirect
// Die App nutzt State-basiertes Routing, daher leiten wir einfach zur Startseite weiter
window.location.replace('/StoryCraft-Studio/');
window.location.replace('/WorldScript-Studio/');
</script>
<noscript>
<meta http-equiv="refresh" content="0; url=/StoryCraft-Studio/">
<meta http-equiv="refresh" content="0; url=/WorldScript-Studio/">
</noscript>
</head>
<body>
Expand Down
30 changes: 15 additions & 15 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -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/",
Comment thread
qnbs marked this conversation as resolved.

"display": "standalone",
"display_override": ["window-controls-overlay", "standalone", "minimal-ui"],
Expand Down Expand Up @@ -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" }]
}
],
Expand All @@ -92,7 +92,7 @@
"handle_links": "preferred",

"share_target": {
"action": "/StoryCraft-Studio/",
"action": "/WorldScript-Studio/",
"method": "GET",
"params": {
"title": "share_title",
Expand All @@ -103,8 +103,8 @@

"protocol_handlers": [
{
"protocol": "web+storycraft",
"url": "/StoryCraft-Studio/?protocol=%s"
"protocol": "web+worldscript",
"url": "/WorldScript-Studio/?protocol=%s"
}
],

Expand Down
8 changes: 4 additions & 4 deletions public/offline.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
<meta name="theme-color" content="#020617" />
<title>Offline — StoryCraft Studio</title>
<title>Offline — WorldScript Studio</title>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

Expand Down Expand Up @@ -168,11 +168,11 @@

<h1>Keine Internetverbindung</h1>
<p>
StoryCraft Studio ist gerade offline. Alle deine gespeicherten Geschichten und Charaktere
WorldScript Studio ist gerade offline. Alle deine gespeicherten Geschichten und Charaktere
sind weiterhin in deinem Browser verfügbar.
</p>

<a href="/StoryCraft-Studio/" class="btn">
<a href="/WorldScript-Studio/" class="btn">
<svg width="16" height="16" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true">
<path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12l8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25"/>
</svg>
Expand All @@ -185,7 +185,7 @@ <h1>Keine Internetverbindung</h1>
<script>
// Auto-reload when connectivity is restored
window.addEventListener('online', () => {
window.location.href = '/StoryCraft-Studio/';
window.location.href = '/WorldScript-Studio/';
});
</script>
</body>
Expand Down
24 changes: 12 additions & 12 deletions public/sw.js
Original file line number Diff line number Diff line change
@@ -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$/, '');
Expand Down Expand Up @@ -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('<!doctype html><title>Offline</title><p>StoryCraft Studio ist offline.</p>', {
return cached || new Response('<!doctype html><title>Offline</title><p>WorldScript Studio ist offline.</p>', {
headers: { 'Content-Type': 'text/html' },
status: 503,
});
Expand Down Expand Up @@ -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],
Expand All @@ -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)
);
});

Expand All @@ -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' }))
Expand All @@ -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') {
Comment thread
qnbs marked this conversation as resolved.
Comment thread
qnbs marked this conversation as resolved.
event.waitUntil(
caches.open(CACHE_DYNAMIC).then(async (cache) => {
const keys = await cache.keys();
Expand All @@ -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`);
Loading
Loading