Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
9dfde0a
feature: provide progress state of page to sidebar
lebalz Oct 15, 2025
a1a499a
make linter happy
lebalz Oct 15, 2025
5536736
Merge branch 'main' into feature/page-progress-state
lebalz Oct 15, 2025
f785608
Merge branch 'main' into feature/page-progress-state
lebalz Oct 19, 2025
3464f52
basic exporter
lebalz Oct 19, 2025
2cd2a9f
account border width in html sandbox
lebalz Oct 20, 2025
b114f15
fix missing null check
lebalz Oct 20, 2025
718b7a2
Merge branch 'main' into feature/page-progress-state
lebalz Oct 31, 2025
7f89faf
add experiments
lebalz Nov 3, 2025
62ed118
Merge branch 'main' into feature/page-progress-state
lebalz Dec 19, 2025
4634aab
fix docusaurus config
lebalz Dec 19, 2025
52378a1
Merge branch 'main' into feature/page-progress-state
lebalz Jan 26, 2026
1e37506
poc of gathering doc ids
lebalz Jan 26, 2026
fae0d57
better config option
lebalz Jan 27, 2026
4bed87a
Merge branch 'feature/gather-doc-ids' into feature/page-progress-state
lebalz Jan 27, 2026
df06f79
work to synchronize pocs
lebalz Jan 27, 2026
e66da9e
poc
lebalz Jan 27, 2026
c5d82bd
load pageStore on load
lebalz Jan 27, 2026
2ed6473
refactor page-progress-state
lebalz Jan 27, 2026
03b52e4
generate Page models from loaded index
lebalz Jan 28, 2026
e6f8646
create page tree
lebalz Jan 28, 2026
ba0d1cb
update loading behavior
lebalz Jan 28, 2026
6a99db5
add init load
lebalz Jan 28, 2026
ca063f7
poc
lebalz Jan 28, 2026
4c6db53
add taskableDocs
lebalz Jan 29, 2026
21bc5f0
load docs on the fly
lebalz Jan 29, 2026
e9bf794
debug print metas
lebalz Jan 29, 2026
66a15c9
update page position calculation
lebalz Jan 29, 2026
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: 4 additions & 2 deletions docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
socketIoNoDepWarningsPluginConfig,
aliasConfigurationPlugin
} from './src/siteConfig/pluginConfigs';
import persistableDocuments from './packages/tdev/page-progress-state/plugin';
import { useTdevContentPath } from './src/siteConfig/helpers';
import path from 'path';
import {
Expand Down Expand Up @@ -383,7 +384,8 @@ const docusaurusConfig = withSiteConfig().then(async (siteConfig) => {
...(siteConfig.pages || {})
}
],
...((siteConfig.plugins as Config['plugins']) || [])
...((siteConfig.plugins as Config['plugins']) || []),
persistableDocuments
],
themes: [
'@docusaurus/theme-mermaid',
Expand All @@ -405,4 +407,4 @@ const docusaurusConfig = withSiteConfig().then(async (siteConfig) => {
return config;
});

export default docusaurusConfig;
export default docusaurusConfig;
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@
"@docusaurus/plugin-rsdoctor": "^3.9.2",
"@docusaurus/tsconfig": "^3.9.2",
"@docusaurus/types": "^3.9.2",
"@types/better-sqlite3": "^7.6.13",
"@types/exceljs": "^1.3.0",
"@types/fs-extra": "^11.0.4",
"@types/js-yaml": "^4.0.9",
Expand All @@ -104,6 +105,7 @@
"@types/uuid": "^10.0.0",
"@types/wicg-file-system-access": "^2023.10.6",
"@vitest/coverage-v8": "^2.0.5",
"better-sqlite3": "^12.6.2",
"concurrently": "^9.2.1",
"fs-extra": "^11.2.0",
"prettier": "^3.3.2",
Expand Down
2 changes: 1 addition & 1 deletion packages/tdev/brython-code/README.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
page_id: e7bde0ab-c0fe-4225-8325-e4ebc22d414b
page_id: 0f9fe018-9c46-4ec0-be50-c2ae69a6a005
tags:
- '@tdev/'
- python
Expand Down
1 change: 1 addition & 0 deletions packages/tdev/page-progress-state/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.assets
11 changes: 11 additions & 0 deletions packages/tdev/page-progress-state/README.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
page_id: cfbd9607-c2d5-4c2a-82e0-9f39c5756747
tags:
- '@tdev/'
---
import { Comp } from '@tdev-components/Demo';

# page-progress-state


<Comp />
21 changes: 21 additions & 0 deletions packages/tdev/page-progress-state/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { DocumentType } from '@tdev-api/document';

export interface PageIndex {
/**
* the document root id
*/
id: string;
type: DocumentType;
/**
* the page_id in the frontmatter of each md/mdx file
*/
page_id: string;
/**
* The resolved path to the file - should be the same
* as docusaurus `path` field in the sidebar index.
*/
path: string;
position: number;
}

export const PluginName = 'page-progress-state';
15 changes: 15 additions & 0 deletions packages/tdev/page-progress-state/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "@tdev/page-progress-state",
"version": "1.0.0",
"main": "index.ts",
"types": "index.ts",
"dependencies": { },
"devDependencies": {
"vitest": "*",
"@docusaurus/module-type-aliases": "*",
"@docusaurus/core": "*"
},
"peerDependencies": {
"@tdev/core": "1.0.0"
}
}
35 changes: 35 additions & 0 deletions packages/tdev/page-progress-state/plugin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { LoadContext, Plugin, PluginModule } from '@docusaurus/types';
import { exportDB } from './utils/exportDb';
import path from 'path';
import { promises as fs } from 'fs';
import { PluginName } from '.';
import { pageIndexPath } from './utils/options';

const isDev = process.env.NODE_ENV !== 'production';

const persistableDocuments: PluginModule = (context: LoadContext) => {
const config: Plugin<{}> = {
name: PluginName,
async allContentLoaded() {
if (isDev) {
try {
await fs.access(path.dirname(pageIndexPath));
} catch {
await fs.mkdir(path.dirname(pageIndexPath), { recursive: true });
}
}
},
async postBuild() {
try {
await fs.access(path.dirname(pageIndexPath));
} catch {
await fs.mkdir(path.dirname(pageIndexPath), { recursive: true });
}

await exportDB();
}
};
return config as Plugin;
};

export default persistableDocuments;
140 changes: 140 additions & 0 deletions packages/tdev/page-progress-state/remark-plugin/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import type { Plugin, Transformer } from 'unified';
import type { Code, Root } from 'mdast';
import type { MdxJsxFlowElement, MdxJsxTextElement } from 'mdast-util-mdx';
import path from 'path';
import db from '../utils/db';
import { exportDB } from '../utils/exportDb';
import { debounce } from 'es-toolkit/function';

const projectRoot = process.cwd();
const isDev = process.env.NODE_ENV !== 'production';

const insertDocRoot = db.prepare(
`INSERT INTO document_roots (
id,
type,
page_id,
path,
position
) VALUES (
@id,
@type,
@page_id,
@path,
@position
) ON CONFLICT(id, path) DO NOTHING`
);

interface JsxConfig<T extends MdxJsxFlowElement | MdxJsxTextElement = MdxJsxFlowElement | MdxJsxTextElement> {
/**
* Component Name
*/
name: string;
/**
* @default id
*/
attributeName?: string;
docTypeExtractor: (node: T) => string;
}

export interface PluginOptions {
components: JsxConfig[];
persistedCodeType?: (code: Code) => string;
docsParentDir?: string;
}

const slugCountMap = new Map<string, number>();

const scheduleExportDb = debounce(
async () => {
await exportDB();
},
250,
{ edges: ['trailing'] }
);

/**
* This plugin transforms inline code and code blocks in MDX files to use
* custom MDX components by converting the code content into attributes.
*/
const remarkPlugin: Plugin<PluginOptions[], Root> = function plugin(
options = { components: [], persistedCodeType: () => 'code', docsParentDir: '' }
): Transformer<Root> {
const { components } = options;
const mdxJsxComponents = new Map<string, JsxConfig>(components.map((c) => [c.name, c]));
const replaceStart = new RegExp(`^${options.docsParentDir}`);
return async (root, file) => {
const { page_id } = (file.data?.frontMatter || {}) as { page_id?: string };
if (components.length < 1 || !page_id) {
return;
}
const { visit, CONTINUE } = await import('unist-util-visit');
const filePath = `/${path.relative(projectRoot, file.path)}`
.replace(/\/(index|README)\.mdx?$/i, '/')
.replace(/\.mdx?$/i, '/')
.replace(replaceStart, '')
.replace(/^\/versioned_docs\/version-/, '/');
slugCountMap.set(filePath, 1);

insertDocRoot.run({
id: page_id,
type: '<page>',
page_id: page_id,
path: filePath,
position: 0
});

visit(root, (node, index, parent) => {
if (node.type === 'code') {
const idMatch = /id=([a-zA-Z0-9-_]+)/.exec(node.meta || '');
if (!idMatch) {
return CONTINUE;
}
const docId = idMatch[1];
const docType = options.persistedCodeType?.(node) ?? 'code';
const res = insertDocRoot.run({
id: docId,
type: docType,
page_id: page_id,
path: filePath,
position: slugCountMap.get(filePath)!
});
if (res.changes > 0) {
slugCountMap.set(filePath, slugCountMap.get(filePath)! + 1);
}
return CONTINUE;
}
if (
(node.type !== 'mdxJsxFlowElement' && node.type !== 'mdxJsxTextElement') ||
!mdxJsxComponents.has(node.name as string)
) {
return CONTINUE;
}
const config = mdxJsxComponents.get(node.name!)!;
const attr = node.attributes.find(
(a) => a.type === 'mdxJsxAttribute' && a.name === (config.attributeName || 'id')
);
if (!attr || attr.type !== 'mdxJsxAttribute' || typeof attr.value !== 'string') {
return CONTINUE;
}
const docId = attr.value;
const docType = config.docTypeExtractor(node);
const res = insertDocRoot.run({
id: docId,
type: docType,
page_id: page_id,
path: filePath,
position: slugCountMap.get(filePath)!
});
if (res.changes > 0) {
slugCountMap.set(filePath, slugCountMap.get(filePath)! + 1);
}
return CONTINUE;
});
if (isDev) {
scheduleExportDb();
}
};
};

export default remarkPlugin;
3 changes: 3 additions & 0 deletions packages/tdev/page-progress-state/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"extends": "../../../tsconfig.json"
}
27 changes: 27 additions & 0 deletions packages/tdev/page-progress-state/utils/db.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import path from 'path';
import Database from 'better-sqlite3';
import { accessSync, mkdirSync } from 'fs';
import { dbPath } from './options';

try {
accessSync(path.dirname(dbPath));
} catch {
mkdirSync(path.dirname(dbPath), { recursive: true });
}

const db = new Database(dbPath, { fileMustExist: false });
db.pragma('journal_mode = WAL');

const createDocRoots = db.prepare(
`CREATE TABLE IF NOT EXISTS document_roots (
id TEXT NOT NULL,
type TEXT NOT NULL,
page_id TEXT NOT NULL,
path TEXT NOT NULL,
position INTEGER NOT NULL,
UNIQUE(id, path)
)`
);
createDocRoots.run();

export default db;
15 changes: 15 additions & 0 deletions packages/tdev/page-progress-state/utils/exportDb.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import db from './db';
import { promises as fs } from 'fs';
import { pageIndexPath } from './options';
import { PageIndex } from '..';

const getDocumentRoots = db.prepare('SELECT * FROM document_roots ORDER BY path ASC, position ASC');

export const getContent = () => {
const documentRoots = getDocumentRoots.all() as PageIndex[];
return { documentRoots };
};

export const exportDB = async () => {
await fs.writeFile(pageIndexPath, JSON.stringify(getContent(), null, 2));
};
18 changes: 18 additions & 0 deletions packages/tdev/page-progress-state/utils/options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import path from 'path';
import { PluginName } from '..';

// current file's directory:
export const pluginRootDir = path.dirname(new URL(import.meta.url).pathname);
export const assetDir = path.join(pluginRootDir, 'assets');

const projectRoot = process.cwd();
const isDev = process.env.NODE_ENV !== 'production';

export const generatedDbDir = path.join(projectRoot, '.docusaurus', PluginName, 'default');

export const generatedDataDir = isDev
? path.join(projectRoot, 'static/tdev-artifacts', PluginName)
: path.join(projectRoot, 'build/tdev-artifacts', PluginName);

export const dbPath = path.join(generatedDbDir, 'index.db');
export const pageIndexPath = path.join(generatedDataDir, 'pageIndex.json');
2 changes: 1 addition & 1 deletion src/components/Admin/EditUser/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ const EditUser = observer((props: Props) => {
setSpinState('deleting');
authClient.admin.removeUser({ userId: user.id }).then(
action((res) => {
if (res.data?.success) {
if (res?.data?.success) {
userStore.removeFromStore(user.id);
props.close();
}
Expand Down
16 changes: 16 additions & 0 deletions src/components/Answer/helper.answer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { DocumentType } from '@tdev-api/document';

export const getAnswerDocumentType = (type: string): DocumentType => {
switch (type) {
case 'text':
return 'quill_v2';
case 'string':
return 'string';
case 'state':
return 'task_state';
case 'progress':
return 'progress_state';
default:
return type as DocumentType;
}
};
Loading