Skip to content

Commit 1e4b04b

Browse files
authored
Release 0.20.1
Merge pull request #782 from dreamteamprod/dev
2 parents b64f049 + 3614067 commit 1e4b04b

File tree

34 files changed

+2227
-43
lines changed

34 files changed

+2227
-43
lines changed

.dockerignore

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
.github
33
reporting
44
scripts
5-
docs
65
documentation
76
dist
87
hooks

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
.claude
33
CLAUDE.md
44
.idea/
5+
.playwright-mcp/
56

67
# Gradle and Maven with auto-import
78
# When using Gradle or Maven with auto-import, you should exclude module files,

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ COPY /client/package-lock.json /client/package-lock.json
88
WORKDIR /client
99
RUN npm ci
1010
COPY /client /client
11+
COPY /docs /docs
1112
RUN npm run build
1213

1314
COPY /server /server

client/.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,6 @@ pnpm-debug.log*
2222
*.njsproj
2323
*.sln
2424
*.sw?
25+
26+
# Generated documentation (copied from /docs during build)
27+
public/docs/

client/package-lock.json

Lines changed: 46 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "client",
3-
"version": "0.20.0",
3+
"version": "0.20.1",
44
"private": true,
55
"scripts": {
6+
"prebuild": "scripts/copy-docs.sh && node scripts/generate-doc-manifest.js",
67
"build": "vite build",
78
"lint": "eslint 'src/**/*.{js,vue}' --fix",
89
"ci-lint": "eslint 'src/**/*.{js,vue}'",
@@ -19,9 +20,12 @@
1920
"contrast-color": "1.0.1",
2021
"core-js": "3.47.0",
2122
"deep-object-diff": "1.1.9",
23+
"dompurify": "3.3.1",
24+
"fuse.js": "7.1.0",
2225
"jquery": "3.7.1",
2326
"lodash": "4.17.21",
2427
"loglevel": "1.9.2",
28+
"marked": "11.2.0",
2529
"vue": "2.7.14",
2630
"vue-multiselect": "2.1.9",
2731
"vue-native-websocket": "2.0.15",
@@ -44,7 +48,7 @@
4448
"eslint-plugin-vue": "^9.27.0",
4549
"eslint-plugin-vuejs-accessibility": "1.2.0",
4650
"node-sass": "7.0.3",
47-
"sass": "1.95.0",
51+
"sass": "1.97.0",
4852
"sass-loader": "13.3.3",
4953
"vite": "4.5.5"
5054
},

client/scripts/copy-docs.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#!/bin/bash
2+
set -e
3+
4+
DOCS_SOURCE="../docs"
5+
DOCS_DEST="./public/docs"
6+
7+
echo "Copying documentation assets..."
8+
9+
# Remove existing docs
10+
rm -rf "$DOCS_DEST"
11+
12+
# Copy docs directory
13+
cp -r "$DOCS_SOURCE" "$DOCS_DEST"
14+
15+
echo "Documentation assets copied successfully"
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
#!/usr/bin/env node
2+
const fs = require('fs');
3+
const path = require('path');
4+
5+
const DOCS_DIR = path.join(__dirname, '../../docs');
6+
const OUTPUT_DIR = path.join(__dirname, '../public/docs');
7+
const MANIFEST_PATH = path.join(OUTPUT_DIR, 'manifest.json');
8+
9+
function generateSlug(filepath) {
10+
// pages/getting_started.md → getting-started
11+
// pages/show_config/acts_and_scenes.md → show-config/acts-and-scenes
12+
const withoutExt = filepath.replace(/\.md$/, '');
13+
// Remove 'pages/' prefix if present
14+
const withoutPages = withoutExt.replace(/^pages\//, '');
15+
return withoutPages.replace(/_/g, '-');
16+
}
17+
18+
function extractTitle(content, filepath) {
19+
// For nested docs (e.g., show_config/*), extract H3 title
20+
// For root docs, extract H1/H2 title
21+
const parts = filepath.split('/');
22+
23+
if (parts.length > 2) {
24+
// Nested document - try to extract H3 first
25+
const h3Match = content.match(/^###\s+(.+)$/m);
26+
if (h3Match) {
27+
return h3Match[1];
28+
}
29+
}
30+
31+
// Fall back to H1/H2
32+
const match = content.match(/^##?\s+(.+)$/m);
33+
if (match) {
34+
return match[1];
35+
}
36+
37+
// Last resort: generate from filename
38+
const filename = parts[parts.length - 1].replace(/\.md$/, '');
39+
return filename
40+
.replace(/_/g, ' ')
41+
.replace(/\b\w/g, (c) => c.toUpperCase());
42+
}
43+
44+
function extractCategory(relativePath) {
45+
// pages/getting_started.md → root
46+
// pages/show_config/acts_and_scenes.md → show_config
47+
const parts = relativePath.split('/');
48+
if (parts.length > 2) {
49+
return parts[1]; // Return subdirectory name
50+
}
51+
return 'root';
52+
}
53+
54+
function extractMarkdownLinks(content) {
55+
// Extract all markdown links: [text](./path.md) or [text](path.md)
56+
const linkRegex = /\[([^\]]+)\]\(([^)]+\.md)\)/g;
57+
const links = [];
58+
let match;
59+
60+
while ((match = linkRegex.exec(content)) !== null) {
61+
links.push(match[2]); // The URL part
62+
}
63+
64+
return links;
65+
}
66+
67+
function normalizeLink(currentFile, link) {
68+
// Convert relative link to absolute path within docs
69+
// currentFile: 'index.md' or 'pages/getting_started.md'
70+
// link: './pages/getting_started.md' or '../pages/show_config.md'
71+
72+
const currentDir = path.dirname(currentFile);
73+
const absolutePath = path.join(currentDir, link);
74+
75+
// Normalize path (resolve .. and .)
76+
const normalized = path.normalize(absolutePath);
77+
78+
// Convert backslashes to forward slashes for consistency
79+
return normalized.replace(/\\/g, '/');
80+
}
81+
82+
function buildOrderFromLinks(docsDir) {
83+
const visited = new Set();
84+
const order = [];
85+
86+
function traverse(relativePath) {
87+
// Skip if already visited
88+
if (visited.has(relativePath)) {
89+
return;
90+
}
91+
92+
// Check if file exists
93+
const fullPath = path.join(docsDir, relativePath);
94+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
95+
return;
96+
}
97+
98+
visited.add(relativePath);
99+
order.push(relativePath);
100+
101+
// Read file and extract links
102+
const content = fs.readFileSync(fullPath, 'utf8');
103+
const links = extractMarkdownLinks(content);
104+
105+
// Recursively traverse each link
106+
for (const link of links) {
107+
const normalizedLink = normalizeLink(relativePath, link);
108+
traverse(normalizedLink);
109+
}
110+
}
111+
112+
// Start traversal from index.md
113+
traverse('index.md');
114+
115+
return order;
116+
}
117+
118+
function walkDocs(dir, basePath = '') {
119+
const manifest = [];
120+
121+
// Check if directory exists
122+
if (!fs.existsSync(dir)) {
123+
console.warn(`Warning: Directory ${dir} does not exist`);
124+
return manifest;
125+
}
126+
127+
const entries = fs.readdirSync(dir, { withFileTypes: true });
128+
129+
for (const entry of entries) {
130+
const fullPath = path.join(dir, entry.name);
131+
const relativePath = path.join(basePath, entry.name);
132+
133+
if (entry.isDirectory() && entry.name !== 'images') {
134+
// Recursively walk subdirectories
135+
manifest.push(...walkDocs(fullPath, relativePath));
136+
} else if (entry.isFile() && entry.name.endsWith('.md')) {
137+
const content = fs.readFileSync(fullPath, 'utf8');
138+
const title = extractTitle(content, relativePath);
139+
const slug = generateSlug(relativePath);
140+
const category = extractCategory(relativePath);
141+
142+
manifest.push({
143+
title,
144+
slug,
145+
path: relativePath.replace(/\\/g, '/'),
146+
category,
147+
});
148+
}
149+
}
150+
151+
return manifest;
152+
}
153+
154+
// Ensure output directory exists
155+
if (!fs.existsSync(OUTPUT_DIR)) {
156+
fs.mkdirSync(OUTPUT_DIR, { recursive: true });
157+
}
158+
159+
console.log('Generating documentation manifest...');
160+
161+
// Build link order by traversing from index.md
162+
const linkOrder = buildOrderFromLinks(DOCS_DIR);
163+
console.log(`📊 Discovered ${linkOrder.length} documents via link traversal`);
164+
165+
// Walk all docs to get complete manifest
166+
const manifest = walkDocs(DOCS_DIR);
167+
168+
// Create order map from link traversal
169+
const orderMap = new Map();
170+
linkOrder.forEach((path, index) => {
171+
orderMap.set(path, index);
172+
});
173+
174+
// Add order to each manifest entry
175+
manifest.forEach((entry) => {
176+
const order = orderMap.get(entry.path);
177+
entry.order = order !== undefined ? order : 999;
178+
});
179+
180+
// Filter out index.md since we have a navigation sidebar instead
181+
const filteredManifest = manifest.filter((entry) => entry.path !== 'index.md');
182+
183+
// Sort manifest by order, then by category and title
184+
filteredManifest.sort((a, b) => {
185+
// First, sort by link order
186+
if (a.order !== b.order) {
187+
return a.order - b.order;
188+
}
189+
// Then by category
190+
if (a.category !== b.category) {
191+
return a.category.localeCompare(b.category);
192+
}
193+
// Finally by title
194+
return a.title.localeCompare(b.title);
195+
});
196+
197+
fs.writeFileSync(MANIFEST_PATH, JSON.stringify(filteredManifest, null, 2));
198+
console.log(`✅ Generated manifest with ${filteredManifest.length} documents (excluded index.md)`);
199+
console.log(`📄 Manifest saved to: ${MANIFEST_PATH}`);

0 commit comments

Comments
 (0)