Skip to content

Commit 16627f3

Browse files
committed
refactor(content): rebuild publishing around revision trees
1 parent c55f97b commit 16627f3

180 files changed

Lines changed: 9014 additions & 5944 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/admin/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pnpm dev
2727
pnpm build # production build → dist/
2828
pnpm test # vitest (happy-dom)
2929
pnpm typecheck # tsc --noEmit
30-
pnpm lint # oxlint
30+
pnpm lint # eslint
3131
```
3232

3333
For the full stack, run the core server alongside (`pnpm dev` at the repo root), then bridge the production-served admin to the local dev server via `http://localhost:2333/proxy/qaqdmin/dev-proxy`.

apps/admin/package.json

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@
66
"dev": "NODE_ENV=development vite --mode development --open --host",
77
"build": "NODE_ENV=production vite build --mode production",
88
"preview": "vite preview --port 2323",
9-
"lint": "oxlint src",
10-
"lint:fix": "oxlint src --fix",
9+
"lint": "eslint src",
10+
"lint:fix": "eslint src --fix",
1111
"test": "vitest --run",
1212
"typecheck": "tsc --noEmit"
1313
},
1414
"dependencies": {
1515
"@6over3/zeroperl-ts": "^1.0.10",
16-
"@antv/g2": "^5.4.8",
1716
"@base-ui/react": "1.7.0",
1817
"@better-auth/passkey": "1.6.27",
1918
"@codemirror/commands": "6.11.0",
@@ -68,9 +67,6 @@
6867
"@mx-space/ws-client": "workspace:*",
6968
"@pierre/diffs": "1.3.6",
7069
"@rehookify/datepicker": "6.6.8",
71-
"@simplewebauthn/browser": "13.3.0",
72-
"@tanstack/query-async-storage-persister": "5.102.8",
73-
"@tanstack/query-persist-client-core": "5.102.8",
7470
"@tanstack/react-query": "5.102.8",
7571
"@types/canvas-confetti": "1.9.0",
7672
"@uswriting/exiftool": "^1.0.9",
@@ -88,24 +84,19 @@
8884
"fuse.js": "7.5.0",
8985
"immer": "11.1.18",
9086
"jotai": "2.20.3",
91-
"js-cookie": "3.0.8",
9287
"js-yaml": "5.4.1",
9388
"json5": "2.2.3",
9489
"katex": "^0.18.4",
9590
"klinecharts": "10.0.3",
9691
"lexical": "^0.49.0",
9792
"lit": "3.3.3",
98-
"lodash.transform": "4.6.0",
9993
"lucide-react": "1.35.0",
10094
"maplibre-gl": "6.6.0",
10195
"marked": "18.0.11",
10296
"monaco-editor": "0.56.0",
10397
"motion": "13.1.1",
104-
"ofetch": "1.5.1",
10598
"os-browserify": "0.3.0",
10699
"path-browserify": "1.0.1",
107-
"qier-progress": "1.0.4",
108-
"qs": "6.15.3",
109100
"react": "19.2.8",
110101
"react-dom": "19.2.8",
111102
"react-resizable-panels": "4.12.3",
@@ -117,7 +108,6 @@
117108
"tailwind-merge": "^3.6.0",
118109
"thumbhash": "0.1.1",
119110
"tinykeys": "4.0.0",
120-
"validator": "13.15.35",
121111
"xss": "1.0.15",
122112
"zod": "4.4.3",
123113
"zustand": "5.0.15"
@@ -131,21 +121,16 @@
131121
"@types/babel__core": "7.20.5",
132122
"@types/event-source-polyfill": "1.0.5",
133123
"@types/js-yaml": "4.0.9",
134-
"@types/qs": "6.15.1",
135124
"@types/react": "19.2.18",
136125
"@types/react-dom": "19.2.5",
137-
"@types/validator": "13.15.10",
138126
"@vitejs/plugin-react": "6.1.1",
139127
"babel-plugin-react-compiler": "1.0.0",
140128
"code-inspector-plugin": "2.0.8",
141-
"cors": "2.8.6",
142129
"happy-dom": "^20.11.12",
143-
"rollup": "^4.63.1",
144130
"tailwindcss": "^4.3.3",
145131
"typescript": "6.0.3",
146132
"vite": "8.2.2",
147133
"vite-plugin-checker": "0.14.5",
148-
"vite-plugin-mkcert": "2.1.0",
149134
"vitest": "^4.1.11"
150135
}
151136
}

apps/admin/src/api/ai-overview.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ interface AssetBase {
3939
generationMetrics?: GenerationMetrics | null
4040
id: string
4141
lang: string
42+
stale?: boolean
4243
}
4344

4445
export interface SummaryAsset extends AssetBase {

apps/admin/src/api/content-migrations.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import { postJson } from './http'
22

3-
export type MigrationMemberKind = 'draft' | 'source' | 'translation'
3+
export type MigrationMemberKind = 'branch' | 'source' | 'translation'
44

55
export interface MigrationMemberPrecondition {
66
hash: string
77
id: string
88
kind: MigrationMemberKind
9-
version?: number
9+
headRevisionId?: string
1010
}
1111

1212
export interface MarkdownMigrationIssue {
@@ -77,7 +77,7 @@ export interface MarkdownToLexicalMigrationDescriptor {
7777
}
7878

7979
export function dryRunMarkdownToLexical(data: {
80-
draftId?: string
80+
branchId?: string
8181
profile: 'yohaku-v1'
8282
refId: string
8383
refType: 'note' | 'page' | 'post'

apps/admin/src/api/drafts.ts

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import type { Image, PaginateResult } from '~/models/base'
22
import type {
3-
DraftHistoryListItem,
3+
ContentRevision,
44
DraftModel,
55
DraftRefType,
6+
RevisionComparison,
67
TypeSpecificData,
8+
VersionContext,
79
} from '~/models/draft'
810

911
import { deleteJson, getJson, postJson, putJson } from './http'
@@ -14,32 +16,40 @@ export interface GetDraftsParams {
1416
hasRef?: boolean
1517
page?: number
1618
refType?: DraftRefType
19+
search?: string
1720
size?: number
1821
sort_by?: string
1922
sort_order?: DraftSortOrder
2023
}
2124

22-
export interface CreateDraftData {
25+
export interface DraftWriteData {
2326
content?: string
24-
contentFormat?: 'lexical' | 'markdown'
25-
images?: Image[]
26-
meta?: Record<string, unknown>
27+
contentFormat: 'lexical' | 'markdown'
28+
images?: Image[] | null
29+
meta?: Record<string, unknown> | null
30+
text: string
31+
title: string
32+
typeSpecificData?: TypeSpecificData | null
33+
}
34+
35+
export interface CreateDraftData {
36+
baseRevisionId?: string | null
37+
data: DraftWriteData
2738
refId?: string
2839
refType: DraftRefType
29-
text?: string
30-
title?: string
31-
typeSpecificData?: TypeSpecificData
3240
}
3341

34-
export type UpdateDraftData = Partial<CreateDraftData> & {
35-
expectedVersion: number
42+
export interface UpdateDraftData {
43+
data: DraftWriteData
44+
expectedHeadRevisionId: string
3645
}
3746

3847
export function getDrafts(params: GetDraftsParams = {}) {
3948
return getJson<PaginateResult<DraftModel>>('/drafts', {
4049
hasRef: params.hasRef === undefined ? undefined : String(params.hasRef),
4150
page: params.page,
4251
refType: params.refType,
52+
search: params.search,
4353
size: params.size,
4454
sort_by: params.sort_by,
4555
sort_order: params.sort_order,
@@ -50,20 +60,24 @@ export function getDraftById(id: string) {
5060
return getJson<DraftModel>(`/drafts/${id}`)
5161
}
5262

53-
export function getDraftByRef(refType: DraftRefType, refId: string) {
54-
return getJson<DraftModel | null>(`/drafts/by-ref/${refType}/${refId}`)
63+
export function getDraftContext(refType: DraftRefType, refId: string) {
64+
return getJson<VersionContext>(`/drafts/context/${refType}/${refId}`)
5565
}
5666

5767
export function getNewDrafts(refType: DraftRefType) {
58-
return getJson<DraftModel[]>(`/drafts/by-ref/${refType}/new`)
68+
return getJson<DraftModel[]>(`/drafts/new/${refType}`)
5969
}
6070

61-
export function getDraftHistory(id: string) {
62-
return getJson<DraftHistoryListItem[]>(`/drafts/${id}/history`)
71+
export function compareRevisions(leftId: string, rightId: string) {
72+
return getJson<RevisionComparison>(`/drafts/compare/${leftId}/${rightId}`)
6373
}
6474

65-
export function getDraftHistoryVersion(id: string, version: number) {
66-
return getJson<DraftModel>(`/drafts/${id}/history/${version}`)
75+
export function getRevision(id: string) {
76+
return getJson<ContentRevision>(`/drafts/revisions/${id}`)
77+
}
78+
79+
export function getDraftRevisions(id: string) {
80+
return getJson<ContentRevision[]>(`/drafts/${id}/revisions`)
6781
}
6882

6983
export function createDraft(data: CreateDraftData) {
@@ -77,10 +91,3 @@ export function updateDraft(id: string, data: UpdateDraftData) {
7791
export function deleteDraft(id: string) {
7892
return deleteJson<{ success: boolean }>(`/drafts/${id}`)
7993
}
80-
81-
export function restoreDraftVersion(id: string, version: number) {
82-
return postJson<DraftModel, Record<string, never>>(
83-
`/drafts/${id}/restore/${version}`,
84-
{},
85-
)
86-
}

apps/admin/src/api/notes.ts

Lines changed: 10 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
import type { Image, PaginateResult } from '~/models/base'
1+
import type { PaginateResult } from '~/models/base'
22
import type { NoteModel } from '~/models/note'
33

4-
import type { MarkdownToLexicalMigrationDescriptor } from './content-migrations'
5-
import { deleteJson, getJson, patchJson, postJson, putJson } from './http'
4+
import { deleteJson, getJson, patchJson } from './http'
65

76
export type NoteSortKey =
87
'createdAt' | 'modifiedAt' | 'mood' | 'title' | 'weather'
@@ -22,35 +21,11 @@ export interface SearchNotesParams {
2221
size: number
2322
}
2423

25-
export interface CreateNoteData {
26-
bookmark?: boolean
27-
content?: string
28-
contentFormat?: 'lexical' | 'markdown'
29-
coordinates?: null | {
30-
latitude: number
31-
longitude: number
32-
}
33-
draftId?: string
34-
images?: Image[]
35-
isPublished?: boolean
36-
location?: null | string
37-
migration?: MarkdownToLexicalMigrationDescriptor
38-
meta?: Record<string, unknown>
39-
mood?: string
40-
password?: null | string
41-
publicAt?: Date | null | string
42-
skipAiAutoGeneration?: boolean
43-
slug?: string
44-
text: string
45-
title: string
46-
topicId?: null | string
47-
weather?: string
48-
}
49-
5024
export interface PatchNoteData {
51-
[key: string]: unknown
52-
slug?: null | string
25+
bookmark?: boolean
26+
mood?: null | string
5327
topicId?: null | string
28+
weather?: null | string
5429
}
5530

5631
export function getNotes(params: GetNotesParams = {}) {
@@ -77,30 +52,15 @@ export function getNoteById(id: string, params?: { single?: boolean }) {
7752
})
7853
}
7954

80-
export function createNote(data: CreateNoteData) {
81-
return postJson<NoteModel, CreateNoteData>('/notes', data)
82-
}
83-
84-
export function updateNote(id: string, data: Partial<CreateNoteData>) {
85-
return putJson<NoteModel, Partial<CreateNoteData>>(`/notes/${id}`, data)
86-
}
87-
8855
export function patchNote(id: string, data: PatchNoteData) {
8956
return patchJson<NoteModel, PatchNoteData>(`/notes/${id}`, data)
9057
}
9158

92-
export function patchNotePublish(
93-
id: string,
94-
isPublished: boolean,
95-
preparedAiResources?: string[],
96-
) {
97-
return patchJson<
98-
NoteModel,
99-
{ isPublished: boolean; preparedAiResources?: string[] }
100-
>(`/notes/${id}/publish`, {
101-
isPublished,
102-
preparedAiResources,
103-
})
59+
export function patchNotePublish(id: string, isPublished: boolean) {
60+
return patchJson<NoteModel, { isPublished: boolean }>(
61+
`/notes/${id}/publish`,
62+
{ isPublished },
63+
)
10464
}
10565

10666
export function deleteNote(id: string) {

apps/admin/src/api/pages.ts

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,13 @@
1-
import type { Image, PaginateResult } from '~/models/base'
1+
import type { PaginateResult } from '~/models/base'
22
import type { PageModel } from '~/models/page'
33

4-
import type { MarkdownToLexicalMigrationDescriptor } from './content-migrations'
5-
import { deleteJson, getJson, patchJson, postJson, putJson } from './http'
4+
import { deleteJson, getJson, patchJson } from './http'
65

76
export interface GetPagesParams {
87
page?: number
98
size?: number
109
}
1110

12-
export interface CreatePageData {
13-
content?: string
14-
contentFormat?: 'lexical' | 'markdown'
15-
draftId?: string
16-
images?: Image[]
17-
meta?: Record<string, unknown>
18-
migration?: MarkdownToLexicalMigrationDescriptor
19-
order?: number
20-
slug: string
21-
subtitle?: string
22-
text: string
23-
title: string
24-
}
25-
2611
export function getPages(params: GetPagesParams = {}) {
2712
return getJson<PaginateResult<PageModel>>('/pages', {
2813
page: params.page,
@@ -34,14 +19,6 @@ export function getPageById(id: string) {
3419
return getJson<PageModel>(`/pages/${id}`)
3520
}
3621

37-
export function createPage(data: CreatePageData) {
38-
return postJson<PageModel, CreatePageData>('/pages', data)
39-
}
40-
41-
export function updatePage(id: string, data: Partial<CreatePageData>) {
42-
return putJson<PageModel, Partial<CreatePageData>>(`/pages/${id}`, data)
43-
}
44-
4522
export function deletePage(id: string) {
4623
return deleteJson<void>(`/pages/${id}`)
4724
}

0 commit comments

Comments
 (0)