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 Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ COPY /client/.npmrc /client/.npmrc
WORKDIR /client
RUN npm ci
COPY /client /client
COPY /docs /docs
RUN npm run build

FROM node:24-bookworm AS build_v3

RUN mkdir -p /server/static/ui-new
RUN mkdir -p /server/static
COPY /client-v3/package.json /client-v3/package.json
COPY /client-v3/package-lock.json /client-v3/package-lock.json
WORKDIR /client-v3
RUN npm ci
COPY /client-v3 /client-v3
COPY /docs /docs
RUN npm run build

FROM python:3.13-bookworm
Expand All @@ -30,7 +30,7 @@ RUN apt install -y nano

COPY /server /server
COPY --from=build_v2 /server/static /server/static
COPY --from=build_v3 /server/static/ui-new /server/static/ui-new
COPY --from=build_v3 /server/static /server/static
WORKDIR /server
RUN mkdir conf
EXPOSE 8080
Expand Down
2 changes: 1 addition & 1 deletion client-v3/e2e/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type { Page } from '@playwright/test';

export const SERVER_PORT = 8888;
export const UI_BASE = `http://localhost:${SERVER_PORT}/ui-new`;
export const UI_BASE = `http://localhost:${SERVER_PORT}`;
export const ADMIN_USERNAME = 'admin';
export const ADMIN_PASSWORD = 'testpassword';

Expand Down
264 changes: 149 additions & 115 deletions client-v3/package-lock.json

Large diffs are not rendered by default.

19 changes: 10 additions & 9 deletions client-v3/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "client-v3",
"version": "0.31.2",
"version": "0.32.0",
"description": "DigiScript front end (Vue 3)",
"author": "DreamTeamProd",
"private": true,
"type": "module",
"scripts": {
"prebuild": "scripts/copy-docs.sh && node --strip-types scripts/generate-doc-manifest.ts",
"build": "vite build",
"build:analyze": "vite build --mode analyze",
"lint": "npm run format && npm run lint:eslint",
Expand Down Expand Up @@ -41,8 +42,8 @@
"d3-selection": "^3.0.0",
"d3-zoom": "^3.0.0",
"deep-object-diff": "1.1.9",
"dompurify": "^3.4.8",
"fuse.js": "^7.4.1",
"dompurify": "^3.4.9",
"fuse.js": "^7.4.2",
"lodash": "^4.18.1",
"loglevel": "^1.9.2",
"marked": "^18.0.5",
Expand All @@ -55,14 +56,14 @@
"vue-toast-notification": "^3.1.3"
},
"devDependencies": {
"@emnapi/core": "1.10.0",
"@emnapi/runtime": "1.10.0",
"@emnapi/core": "1.11.0",
"@emnapi/runtime": "1.11.0",
"@eslint/js": "^10.0.1",
"@iconify-json/mdi": "^1.2.3",
"@types/lodash": "~4.17.24",
"@types/node": ">=22.12.0",
"@typescript-eslint/eslint-plugin": "^8.60.1",
"@typescript-eslint/parser": "^8.60.1",
"@typescript-eslint/eslint-plugin": "^8.61.0",
"@typescript-eslint/parser": "^8.61.0",
"@vitejs/plugin-vue": "^6.0.7",
"@vitest/ui": "^4.1.8",
"@vue/test-utils": "^2.4.11",
Expand All @@ -73,10 +74,10 @@
"globals": "^17.6.0",
"jiti": "^2.7.0",
"jsdom": "^29.1.1",
"prettier": "^3.8.3",
"prettier": "^3.8.4",
"sass": "1.100.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.1",
"typescript-eslint": "^8.61.0",
"unplugin-icons": "^23.0.1",
"unplugin-vue-components": "^32.1.0",
"vite": "^8.0.16",
Expand Down
16 changes: 16 additions & 0 deletions client-v3/scripts/copy-docs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/bash
set -e

DOCS_SOURCE="../docs"
DOCS_DEST="./public/docs"

echo "Copying documentation assets..."

# Remove existing docs and ensure parent dir exists
rm -rf "$DOCS_DEST"
mkdir -p "$(dirname "$DOCS_DEST")"

# Copy docs directory
cp -r "$DOCS_SOURCE" "$DOCS_DEST"

echo "Documentation assets copied successfully"
135 changes: 135 additions & 0 deletions client-v3/scripts/generate-doc-manifest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env node
import fs from 'fs';

Check warning on line 2 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:fs` over `fs`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0L&open=AZ6x5DdPLnQ0n5ZdbD0L&pullRequest=1176
import path from 'path';

Check warning on line 3 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:path` over `path`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0M&open=AZ6x5DdPLnQ0n5ZdbD0M&pullRequest=1176
import { fileURLToPath } from 'url';

Check warning on line 4 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `node:url` over `url`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0N&open=AZ6x5DdPLnQ0n5ZdbD0N&pullRequest=1176

const __dirname = path.dirname(fileURLToPath(import.meta.url));

const DOCS_DIR = path.join(__dirname, '../../docs');
const OUTPUT_DIR = path.join(__dirname, '../public/docs');
const MANIFEST_PATH = path.join(OUTPUT_DIR, 'manifest.json');

interface ManifestEntry {
title: string;
slug: string;
path: string;
category: string;
order: number;
}

function generateSlug(filepath: string): string {
// pages/getting_started.md → getting-started
// pages/show_config/acts_and_scenes.md → show-config/acts-and-scenes
const withoutExt = filepath.replace(/\.md$/, '');
const withoutPages = withoutExt.replace(/^pages\//, '');
return withoutPages.replace(/_/g, '-');

Check warning on line 25 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0O&open=AZ6x5DdPLnQ0n5ZdbD0O&pullRequest=1176
}

function extractTitle(content: string, filepath: string): string {
const parts = filepath.split('/');

if (parts.length > 2) {
const h3Match = content.match(/^###\s+(.+)$/m);

Check warning on line 32 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0P&open=AZ6x5DdPLnQ0n5ZdbD0P&pullRequest=1176
if (h3Match) return h3Match[1];
}

const match = content.match(/^##?\s+(.+)$/m);

Check warning on line 36 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the "RegExp.exec()" method instead.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0R&open=AZ6x5DdPLnQ0n5ZdbD0R&pullRequest=1176
if (match) return match[1];

const filename = parts[parts.length - 1].replace(/\.md$/, '');

Check warning on line 39 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `.at(…)` over `[….length - index]`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0T&open=AZ6x5DdPLnQ0n5ZdbD0T&pullRequest=1176
return filename.replace(/_/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());

Check warning on line 40 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0U&open=AZ6x5DdPLnQ0n5ZdbD0U&pullRequest=1176
}

function extractCategory(relativePath: string): string {
const parts = relativePath.split('/');
return parts.length > 2 ? parts[1] : 'root';
}

function extractMarkdownLinks(content: string): string[] {
// Extract all markdown links: [text](./path.md) or [text](path.md)
return [...content.matchAll(/\[([^\]]+)\]\(([^)]+\.md)\)/g)].map((m) => m[2]);
}

function normalizeLink(currentFile: string, link: string): string {
const currentDir = path.dirname(currentFile);
return path.normalize(path.join(currentDir, link)).replace(/\\/g, '/');

Check warning on line 55 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0W&open=AZ6x5DdPLnQ0n5ZdbD0W&pullRequest=1176
}

function buildOrderFromLinks(docsDir: string): string[] {
const visited = new Set<string>();
const order: string[] = [];

function traverse(relativePath: string): void {
if (visited.has(relativePath)) return;

const fullPath = path.join(docsDir, relativePath);
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) return;

visited.add(relativePath);
order.push(relativePath);

const content = fs.readFileSync(fullPath, 'utf8');
for (const link of extractMarkdownLinks(content)) {
traverse(normalizeLink(relativePath, link));
}
}

traverse('index.md');
return order;
}

function walkDocs(dir: string, basePath = ''): Omit<ManifestEntry, 'order'>[] {
const manifest: Omit<ManifestEntry, 'order'>[] = [];

if (!fs.existsSync(dir)) {
console.warn(`Warning: Directory ${dir} does not exist`);
return manifest;
}

for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
const relativePath = path.join(basePath, entry.name);

if (entry.isDirectory() && entry.name !== 'images') {
manifest.push(...walkDocs(fullPath, relativePath));
} else if (entry.isFile() && entry.name.endsWith('.md')) {
const content = fs.readFileSync(fullPath, 'utf8');
manifest.push({
title: extractTitle(content, relativePath),
slug: generateSlug(relativePath),
path: relativePath.replace(/\\/g, '/'),

Check warning on line 100 in client-v3/scripts/generate-doc-manifest.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `String#replaceAll()` over `String#replace()`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5DdPLnQ0n5ZdbD0X&open=AZ6x5DdPLnQ0n5ZdbD0X&pullRequest=1176
category: extractCategory(relativePath),
});
}
}

return manifest;
}

if (!fs.existsSync(OUTPUT_DIR)) {
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
}

console.log('Generating documentation manifest...');

const linkOrder = buildOrderFromLinks(DOCS_DIR);
console.log(`📊 Discovered ${linkOrder.length} documents via link traversal`);

const orderMap = new Map<string, number>(linkOrder.map((filePath, i) => [filePath, i]));

const manifest: ManifestEntry[] = walkDocs(DOCS_DIR).map((entry) => ({
...entry,
order: orderMap.get(entry.path) ?? 999,
}));

const filteredManifest = manifest
.filter((entry) => entry.path !== 'index.md')
.sort((a, b) => {
if (a.order !== b.order) return a.order - b.order;
if (a.category !== b.category) return a.category.localeCompare(b.category);
return a.title.localeCompare(b.title);
});

fs.writeFileSync(MANIFEST_PATH, JSON.stringify(filteredManifest, null, 2));
console.log(`✅ Generated manifest with ${filteredManifest.length} documents (excluded index.md)`);
console.log(`📄 Manifest saved to: ${MANIFEST_PATH}`);
6 changes: 4 additions & 2 deletions client-v3/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@
</BNavItem>
</BNavbarNav>
<BNavbarNav class="ms-auto">
<BNavItem v-if="!isElectronEnv" href="/?_switch=1"> Switch to Classic UI </BNavItem>
<BNavItem v-if="!isElectronEnv" href="/ui-old/?_switch=1">
Switch to Classic UI
</BNavItem>
<BNavItem to="/help"> Help </BNavItem>
<BNavItem to="/about"> About </BNavItem>
<BNavItemDropdown v-if="isElectronEnv" text="Server">
Expand Down Expand Up @@ -273,7 +275,7 @@
await Promise.all([userStore.getCurrentRbac(), userStore.getUserSettings()]);
const switching = new URLSearchParams(window.location.search).has('_switch');
if (!switching && (userStore.userSettings as UserSettings).preferred_ui === 'old') {
window.location.href = '/?_switch=1';
window.location.href = '/ui-old/?_switch=1';

Check warning on line 278 in client-v3/src/App.vue

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Prefer `globalThis` over `window`.

See more on https://sonarcloud.io/project/issues?id=dreamteamprod_DigiScript&issues=AZ6x5Db8LnQ0n5ZdbD0K&open=AZ6x5Db8LnQ0n5ZdbD0K&pullRequest=1176
return false;
}
if (switching) {
Expand Down
2 changes: 1 addition & 1 deletion client-v3/src/components/config/ConfigShows.vue
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ const isSubmittingLoad = ref(false);
const isSubmittingShow = ref(false);
const isDeleting = ref(false);
const deletingId = ref<number | null>(null);
const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_shows');

const showFields = [
{ key: 'id', label: 'ID' },
Expand Down
2 changes: 1 addition & 1 deletion client-v3/src/components/config/ConfigSystem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const { connectedSessions, versionStatus, serverInfo } = storeToRefs(systemStore

const loading = ref(true);
const isCheckingVersion = ref(false);
const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_system_clients');
const clientsModal = ref<InstanceType<typeof BModal>>();

const clientFields = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const showStore = useShowStore();
const { confirm } = useConfirm();

const loading = ref(true);
const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_acts');
const submittingNewAct = ref(false);
const submittingEditAct = ref(false);
const deletingAct = ref(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ const showStore = useShowStore();
const { confirm } = useConfirm();

const loading = ref(true);
const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_scenes');
const submittingNewScene = ref(false);
const submittingEditScene = ref(false);
const submittingFirstScene = ref(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ const showStore = useShowStore();
const { confirm } = useConfirm();

const loading = ref(true);
const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_character_groups');
const submittingNewGroup = ref(false);
const submittingEditGroup = ref(false);
const deletingGroup = ref(false);
Expand Down
2 changes: 1 addition & 1 deletion client-v3/src/components/show/config/mics/MicList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const systemStore = useSystemStore();
const { confirm } = useConfirm();
const { validationState } = useFormValidation();

const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_mics');
const isSubmitting = ref(false);

const newModal = ref<InstanceType<typeof BModal>>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ const systemStore = useSystemStore();
const { confirm } = useConfirm();
const { validationState } = useFormValidation();

const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'show_stage_direction_styles');
const isSubmittingNew = ref(false);
const isSubmittingEdit = ref(false);
const isDeleting = ref(false);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ const systemStore = useSystemStore();
const showStore = useShowStore();
const { confirm } = useConfirm();

const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_session_tags');
const isSubmittingNewTag = ref(false);
const isSubmittingEditTag = ref(false);
const isSubmittingDeleteTag = ref(false);
Expand Down
2 changes: 1 addition & 1 deletion client-v3/src/components/show/config/stage/CrewList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ const systemStore = useSystemStore();
const { confirm } = useConfirm();
const { validationState } = useFormValidation();

const { perPage, currentPage } = usePagination();
const { perPage, currentPage } = usePagination(15, 'config_crew');
const isSubmitting = ref(false);

const newModal = ref<InstanceType<typeof BModal>>();
Expand Down
7 changes: 5 additions & 2 deletions client-v3/src/components/show/config/stage/PropsList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -220,8 +220,11 @@ const systemStore = useSystemStore();
const { confirm } = useConfirm();
const { validationState } = useFormValidation();

const { perPage: propTypesPerPage, currentPage: currentPropTypesPage } = usePagination();
const { perPage: propsPerPage, currentPage: currentPropsPage } = usePagination();
const { perPage: propTypesPerPage, currentPage: currentPropTypesPage } = usePagination(
15,
'config_prop_types'
);
const { perPage: propsPerPage, currentPage: currentPropsPage } = usePagination(15, 'config_props');
const isSubmitting = ref(false);

const newPropTypeModal = ref<InstanceType<typeof BModal>>();
Expand Down
10 changes: 8 additions & 2 deletions client-v3/src/components/show/config/stage/SceneryList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -222,8 +222,14 @@ const systemStore = useSystemStore();
const { confirm } = useConfirm();
const { validationState } = useFormValidation();

const { perPage: sceneryTypesPerPage, currentPage: currentSceneryTypesPage } = usePagination();
const { perPage: sceneryPerPage, currentPage: currentSceneryPage } = usePagination();
const { perPage: sceneryTypesPerPage, currentPage: currentSceneryTypesPage } = usePagination(
15,
'config_scenery_types'
);
const { perPage: sceneryPerPage, currentPage: currentSceneryPage } = usePagination(
15,
'config_scenery'
);
const isSubmitting = ref(false);

const newSceneryTypeModal = ref<InstanceType<typeof BModal>>();
Expand Down
10 changes: 8 additions & 2 deletions client-v3/src/components/show/config/stage/StageManager.vue
Original file line number Diff line number Diff line change
Expand Up @@ -450,8 +450,14 @@ const setExpanded = ref(false);
const strikeExpanded = ref(false);
const savingAssignment = ref(false);
const newCrewSelections = ref<Record<string, number | null>>({});
const { perPage: sceneryAllocPerPage, currentPage: currentSceneryAllocPage } = usePagination();
const { perPage: propsAllocPerPage, currentPage: currentPropsAllocPage } = usePagination();
const { perPage: sceneryAllocPerPage, currentPage: currentSceneryAllocPage } = usePagination(
15,
'config_stage_scenery_alloc'
);
const { perPage: propsAllocPerPage, currentPage: currentPropsAllocPage } = usePagination(
15,
'config_stage_props_alloc'
);

const goToSceneModal = ref<InstanceType<typeof BModal>>();
const addSceneryModal = ref<InstanceType<typeof BModal>>();
Expand Down
Loading
Loading