Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
135 commits
Select commit Hold shift + click to select a range
d581e8f
Merge pull request #645 from NagariaHussain/feat/dynamic-tab-titles
NagariaHussain Jun 4, 2026
e729f3d
feat(editor): auto-convert uploaded images to WebP
NagariaHussain Jun 9, 2026
cbf1286
Merge pull request #649 from NagariaHussain/feat/auto-webp-image-conv…
NagariaHussain Jun 10, 2026
0ab97dc
feat(editor): embed PDFs with inline scrollable viewer
NagariaHussain Jun 11, 2026
d5c85b6
fix(ci): suppress false-positive semgrep file-traversal finding
NagariaHussain Jun 13, 2026
6fcaeef
fix(editor): stop infinite markdown loop that froze pages with PDF em…
NagariaHussain Jun 14, 2026
b1b5b7d
fix(editor): correct the real cause of the PDF-embed freeze
NagariaHussain Jun 14, 2026
935a013
fix(editor): apply the same trailing-newline fix to video and iframe …
NagariaHussain Jun 14, 2026
f4f9085
Merge pull request #650 from NagariaHussain/feat/pdf-embeds
NagariaHussain Jun 14, 2026
f999191
perf(wiki-document): add DB index on route field
NagariaHussain Jun 14, 2026
5480dfe
perf(change-request): add composite DB indexes for conflict & CR lookups
NagariaHussain Jun 14, 2026
499c6a0
perf(wiki-document): add DB indexes on lft/rgt for tree range scans
NagariaHussain Jun 14, 2026
25c1808
Merge pull request #652 from NagariaHussain/perf/db-indexes
NagariaHussain Jun 14, 2026
3b50708
fix(pdf-embed): vendor pdfjs as .js so production serves it as JS
NagariaHussain Jun 14, 2026
24e86b8
Merge pull request #653 from NagariaHussain/fix/pdf-embed-mjs-mime
NagariaHussain Jun 14, 2026
cd45ab1
feat(access-control): role-based read/write access for Wiki Spaces
NagariaHussain Jun 16, 2026
75bfa47
fix(access-control): satisfy Semgrep security rules
NagariaHussain Jun 16, 2026
5e019df
refactor(access-control): bootstrap CR revision via ignore_permission…
NagariaHussain Jun 17, 2026
e943990
chore: repo housekeeping
NagariaHussain Jun 17, 2026
c083489
docs(access-control): log regression tests + read-only roles editor i…
NagariaHussain Jun 17, 2026
a6f1c76
test(access-control): regression suite for permission helpers + gate …
NagariaHussain Jun 17, 2026
7628540
feat(access-control): Builder-style Space Settings dialog + published…
NagariaHussain Jun 17, 2026
0a10e64
fix(access-control): simplify published role default to Guest only
NagariaHussain Jun 17, 2026
1e8448c
refactor(access-control): use query builder for wiki_space backfill U…
NagariaHussain Jun 17, 2026
1be1a96
Merge pull request #654 from NagariaHussain/feature/role-based-space-…
NagariaHussain Jun 17, 2026
a1b405a
docs(mermaid): spec for Mermaid diagram editing & embedding
NagariaHussain Jun 18, 2026
8b1219c
feat: support mermaid rendering in wiki pages
adamwu7 May 18, 2026
7cdfab9
feat: add mermaid editing support
adamwu7 May 18, 2026
b4943cc
refactor: share mermaid loader
adamwu7 May 18, 2026
1523168
refactor(mermaid): polished editor UX, theme sync & hardening
NagariaHussain Jun 18, 2026
cc35db5
docs(mermaid): reconcile spec with implementation built on #627
NagariaHussain Jun 18, 2026
e58df05
fix(mermaid): remove blue focus ring on the code pane
NagariaHussain Jun 18, 2026
903be29
fix(mermaid): re-render public diagrams on theme toggle
NagariaHussain Jun 18, 2026
2605199
test(mermaid): end-to-end Playwright coverage
NagariaHussain Jun 18, 2026
a3c83f5
feat(mermaid): help link + fix multi-diagram render collision & stray…
NagariaHussain Jun 18, 2026
5e064e7
style(mermaid): present public diagrams as a centered figure
NagariaHussain Jun 18, 2026
b12d682
fix(drafts): clear local-first IndexedDB drafts on discard/merge
NagariaHussain Jun 18, 2026
2f4a875
chore(deps): update @pierre/diffs to 1.2.11
NagariaHussain Jun 18, 2026
89ba69f
docs(review-flow): spec the change-request review revamp
NagariaHussain Jun 18, 2026
6eef761
docs(review-flow): re-slice plan into tracer bullets
NagariaHussain Jun 18, 2026
c92d9f4
docs(review-flow): reconcile bullet 1 as-built
NagariaHussain Jun 19, 2026
50b3586
feat(review-flow): walking skeleton — self-serve approve & merge
NagariaHussain Jun 19, 2026
2270db9
docs(review-flow): reconcile bullet 2 as-built
NagariaHussain Jun 19, 2026
56a6954
feat(review-flow): reviewer feedback round-trip
NagariaHussain Jun 19, 2026
b4100a9
docs(review-flow): reconcile bullet 3 as-built
NagariaHussain Jun 19, 2026
8c70c27
feat(review-flow): reject (terminal) & withdraw
NagariaHussain Jun 19, 2026
b6828ba
docs(review-flow): reconcile bullet 4 as-built
NagariaHussain Jun 19, 2026
793dd7a
feat(review-flow): native assignment & two-person path
NagariaHussain Jun 19, 2026
ecc3b79
docs(review-flow): reconcile bullet 5 as-built
NagariaHussain Jun 19, 2026
4e4f480
feat(review-flow): notify author on reviewer decisions
NagariaHussain Jun 19, 2026
3cb5559
docs(review-flow): reconcile bullet 6 as-built
NagariaHussain Jun 19, 2026
d71d947
feat(review-flow): rendered preview (preview == production)
NagariaHussain Jun 19, 2026
9727ec7
feat(review-flow): harden editor merge & complete e2e (bullet 7)
NagariaHussain Jun 19, 2026
7d3993a
docs(review-flow): reconcile bullet 7 as-built; mark spec implemented
NagariaHussain Jun 19, 2026
afc8f35
fix(review-flow): break the review <-> preview back-button loop
NagariaHussain Jun 19, 2026
1dd6b04
style(review-flow): fix Change Requests page padding & alignment
NagariaHussain Jun 19, 2026
f96fe04
fix(sidebar): replace history when switching pages within a space
NagariaHussain Jun 19, 2026
d5df273
feat(review-flow): side-by-side preview, assignees, reorder & nav fixes
NagariaHussain Jun 19, 2026
04576dc
Merge pull request #656 from NagariaHussain/chore/update-diffs-library
NagariaHussain Jun 19, 2026
f6fad51
Merge branch 'develop' into feature/review-flow-revamp
NagariaHussain Jun 19, 2026
7aedfc5
feat(spaces): add View button to open a space's public reader
NagariaHussain Jun 19, 2026
aec5f7e
style(spaces): move View button next to the route value
NagariaHussain Jun 19, 2026
0ffa5ef
fix(spaces): straight View column + skeleton on cold load
NagariaHussain Jun 19, 2026
37ac211
test(e2e): update publish flow for Approve-then-Merge UI
NagariaHussain Jun 19, 2026
95400b8
fix(review-flow): repair "Assigned to me" filter and make tabs descri…
NagariaHussain Jun 20, 2026
ebea304
fix(review-flow): select wiki_space so My CR draft rows can route to …
NagariaHussain Jun 20, 2026
205aab3
style(review-flow): tighten CR list columns and compact the date column
NagariaHussain Jun 20, 2026
0bea974
style(review-flow): drop redundant preview subtitle
NagariaHussain Jun 20, 2026
6110afc
fix(review-flow): render CR preview through the TipTap viewer with ed…
NagariaHussain Jun 20, 2026
4f3270f
Merge pull request #657 from NagariaHussain/feature/review-flow-revamp
NagariaHussain Jun 20, 2026
7ac5417
style(mermaid): theme diagrams with Frappe UI design tokens
NagariaHussain Jun 20, 2026
9220f2c
Merge branch 'develop' into feature/mermaid-diagrams
NagariaHussain Jun 20, 2026
55aac07
fix(mermaid): render diagrams in the read-only content viewer
NagariaHussain Jun 20, 2026
979b16a
Merge pull request #627 from adamwu7/codex/mermaid-rendering
NagariaHussain Jun 20, 2026
a527a36
refactor(change-request): remove standalone CR preview page
NagariaHussain Jun 20, 2026
93049eb
feat(permissions): add Beta badge to space permissions settings
NagariaHussain Jun 20, 2026
656d57b
feat(change-request): add Withdraw to the review three-dots menu
NagariaHussain Jun 20, 2026
1fe59ba
Merge pull request #658 from NagariaHussain/fix/cr-preview-only-touch…
NagariaHussain Jun 20, 2026
0758f5d
feat(sidebar): show logged-in user's name in sidebar header
NagariaHussain Jun 20, 2026
0f4a409
Merge pull request #659 from NagariaHussain/feat/sidebar-user-name
NagariaHussain Jun 20, 2026
f33899f
docs(git-sync): add one-way GitHub sync spec and spec-loop runner
NagariaHussain Jun 20, 2026
671a886
feat(git-sync): GitHub→wiki sync engine, fields, and sync_now (TB1a)
NagariaHussain Jun 20, 2026
e456eb2
feat(git-sync): backend read-only enforcement for synced spaces (TB1b-i)
NagariaHussain Jun 20, 2026
46585de
feat(git-sync): read-only synced-space SPA + create-space dialog (TB1…
NagariaHussain Jun 20, 2026
1fe3143
feat(git-sync): edit-on-github action on synced pages (TB2)
NagariaHussain Jun 20, 2026
efd0899
feat(git-sync): .wiki.json structure override for synced spaces (TB3)
NagariaHussain Jun 20, 2026
8c76b02
feat(git-sync): GitHub App settings + token-minter + listing API (TB4a)
NagariaHussain Jun 20, 2026
5c69f22
feat(git-sync): GitHub OAuth connect + repo picker + engine token thr…
NagariaHussain Jun 21, 2026
74dbafe
feat(git-sync): sync log doctype + Git Sync status panel (TB5)
NagariaHussain Jun 21, 2026
c9e51b5
feat(git-sync): real-time webhook sync (TB6)
NagariaHussain Jun 21, 2026
66e7cae
chore(spec-loop): use REST API to update PR body instead of gh pr edit
NagariaHussain Jun 21, 2026
28e8c20
docs(git-sync): spec TB7 — Create GitHub App manifest flow
NagariaHussain Jun 21, 2026
d6d949f
feat(git-sync): Create GitHub App via manifest flow (TB7)
NagariaHussain Jun 21, 2026
770d54d
fix(git-sync): widen route/source_path fields for deep repo trees
NagariaHussain Jun 22, 2026
56f019a
fix(git-sync): drop push events too when manifest has no webhook
NagariaHussain Jun 22, 2026
31ea30a
fix(git-sync): use inactive placeholder hook on non-public hosts
NagariaHussain Jun 22, 2026
3a9afcd
fix(git-sync): commit App credentials in manifest redirect (GET)
NagariaHussain Jun 22, 2026
c401962
feat(git-sync): offer App install when picker has no installations
NagariaHussain Jun 22, 2026
e8df452
fix(git-sync): use native selects for account/repo picker in dialog
NagariaHussain Jun 22, 2026
5094bce
feat(git-sync): ignore dot-dirs + add docs folder input
NagariaHussain Jun 22, 2026
bc0c5fd
fix(git-sync): unique routes from path slug, not title
NagariaHussain Jun 22, 2026
e3e3761
feat(git-sync): persist GitHub user token (no repeated reconnect)
NagariaHussain Jun 22, 2026
0fe63b3
docs(git-sync): spec TB8 (image import) + post-TB7 hardening log
NagariaHussain Jun 22, 2026
36c4f67
feat(git-sync): TB8a — import repo images (Frappe File + WebP) + rewr…
NagariaHussain Jun 22, 2026
ae6ec12
docs(git-sync): spec TB9 — strip YAML front matter + use its title
NagariaHussain Jun 22, 2026
a14934f
feat(git-sync): TB9 — strip YAML front matter + use its title
NagariaHussain Jun 22, 2026
b7d0f8f
ci(git-sync): clear new Semgrep findings
NagariaHussain Jun 23, 2026
ab5df90
fix(git-sync): test mock infinite loop hung CI (repositories pagination)
NagariaHussain Jun 23, 2026
32f1ad8
fix(git-sync): green CI e2e + address PR #660 review
NagariaHussain Jun 25, 2026
47d100e
feat(git-sync): friendlier create dialog, banner, task-list checkboxe…
NagariaHussain Jun 25, 2026
90f893e
feat(git-sync): linkable commit SHA, friendlier status label, GitHub …
NagariaHussain Jun 25, 2026
192524b
feat(git-sync): honour front-matter slug + ordering fields (inference…
NagariaHussain Jun 25, 2026
b6a6a01
fix(mermaid): wait for web fonts before rendering so labels don't clip
NagariaHussain Jun 25, 2026
5782885
fix(mermaid): render node labels as SVG text to stop right-edge clipping
NagariaHussain Jun 25, 2026
dd0d498
refactor(git-sync): .wiki.json lives in the docs folder; nav → sideba…
NagariaHussain Jun 25, 2026
5a9464f
feat(git-sync): Starlight-shaped .wiki.json sidebar (autogenerate + {…
NagariaHussain Jun 25, 2026
6c044b4
feat(git-sync): persist loaded .wiki.json + preview it in the GitHub …
NagariaHussain Jun 25, 2026
ce37391
fix(git-sync): rewrite repo-relative page links to live wiki routes
NagariaHussain Jun 25, 2026
459507c
fix(git-sync): guard tree truncation + uncap get_all queries
NagariaHussain Jun 25, 2026
e046579
Merge remote-tracking branch 'upstream/develop' into feat/github-one-…
NagariaHussain Jun 25, 2026
f710816
feat(git-sync): progressive create dialog + searchable repo/branch pi…
NagariaHussain Jun 25, 2026
321ceb2
fix(git-sync): show loading feedback after ticking "Synced from GitHub?"
NagariaHussain Jun 25, 2026
c40231f
fix(git-sync): repo dropdown opened scrolled, clipping the first option
NagariaHussain Jun 25, 2026
ea1a328
fix(git-sync): repo dropdown no longer clipped by the dialog
NagariaHussain Jun 25, 2026
ef848a1
fix(git-sync): show create-form validation in the dialog, not the con…
NagariaHussain Jun 25, 2026
4fb67db
fix(git-sync): friendlier "pick a repository" validation message
NagariaHussain Jun 25, 2026
1669781
feat(git-sync): show sync-in-progress state in the page area on first…
NagariaHussain Jun 25, 2026
d77b396
feat(git-sync): real-time sync status (replaces polling as primary)
NagariaHussain Jun 25, 2026
b68e835
feat(git-sync): serve root README/index at the space route, not /<spa…
NagariaHussain Jun 25, 2026
07d6690
refactor(git-sync): explicit is_landing flag for group landings (not …
NagariaHussain Jun 25, 2026
da2c990
refactor(git-sync): root index/README is a landing leaf routed at the…
NagariaHussain Jun 25, 2026
ede520b
ci(git-sync): mark intentional site-wide sync realtime (nosemgrep)
NagariaHussain Jun 25, 2026
a20f172
feat(git-sync): persist GitHub webhook deliveries in a log doctype
NagariaHussain Jun 25, 2026
4f4715c
fix(git-sync): address Greptile P1s + semgrep manual-commit suppression
NagariaHussain Jun 25, 2026
3ae1717
fix(git-sync): make webhook delivery idempotent on replay
NagariaHussain Jun 25, 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
4 changes: 4 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Always load and user frappe-app-dev skills.

Unless mentioned, the site is wiki.localhost with Administrator/admin credentials.

## Planning / Spec-ing

Use Tracer bullets comes from the Pragmatic Programmer. When building systems, you want to write code that gets you feedback as quickly as possible. Tracer bullets are small slices of functionality that go through all layers of the system, allowing you to test and validate your approach early. This helps in identifying potential issues and ensures that the overall architecture is sound before investing significant time in development.

## Implementation Guidelines

* Create a new branch before working on a new feature/spec (branch name patterns: feat/, fix/, just like conventional commit pre-fixes)
Expand Down
4 changes: 3 additions & 1 deletion e2e/helpers/frappe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,9 @@ export async function getList<T = Record<string, unknown>>(
if (options.filters) {
params.set('filters', JSON.stringify(options.filters));
}
if (options.limit) {
if (options.limit !== undefined) {
// 0 means "no limit" in Frappe — without this, the REST default of 20
// silently truncates large result sets.
params.set('limit_page_length', options.limit.toString());
}
if (options.orderBy) {
Expand Down
26 changes: 26 additions & 0 deletions e2e/helpers/wiki.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,32 @@
import { APIRequestContext, type Page, expect } from '@playwright/test';
import { createDoc, deleteDoc, getDoc, getList } from './frappe';

/**
* Tear down every Wiki Space with the given (test-unique) route.
*
* `Wiki Space` cascades its documents, revisions and root group on delete (see
* its `on_trash`), so a single atomic `deleteDoc` removes the whole space —
* important for a git-synced space, which is read-only and otherwise lingers as
* the newest entry in the `/wiki` list (ordered by creation desc), trapping
* later specs whose helpers author into the first space (no "New Page" button).
*
* Resolving by route rather than the create response means a space the server
* created but whose response the client never saw — a `createDoc` that timed
* out, or a Playwright retry that created a second space — is still cleaned up.
*/
export async function cleanupWikiSpacesByRoute(
request: APIRequestContext,
route: string,
): Promise<void> {
const found = await getList<{ name: string }>(request, 'Wiki Space', {
fields: ['name'],
filters: { route },
limit: 0,
}).catch(() => []);
for (const space of found)
await deleteDoc(request, 'Wiki Space', space.name).catch(() => {});
}

/**
* Publish the change request currently open on the review page. The header
* primary action is Approve-only; the combined "Approve & Merge" lives in the
Expand Down
94 changes: 94 additions & 0 deletions e2e/tests/git-sync-edit-on-github.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import { expect, test } from '@playwright/test';
import { createDoc } from '../helpers/frappe';
import {
type WikiDocument,
type WikiSpace,
cleanupWikiSpacesByRoute,
} from '../helpers/wiki';

/**
* TB2 — "Edit on GitHub" on a synced page.
*
* A git-synced page carries a repo-relative source_path. Its three-dots menu
* should offer "Edit on GitHub", opening the source file in GitHub's web editor
* at https://github.com/{repo}/edit/{branch}/{source_path}. We seed the space +
* pages via the API (last_sync_time set so no real GitHub fetch fires) and stub
* window.open to assert the exact URL without leaving the app.
*/
test.describe('Git-synced space — Edit on GitHub (TB2)', () => {
const REPO = 'frappe/wiki';
const BRANCH = 'main';

let route: string;

test.afterEach(async ({ request }) => {
if (route) await cleanupWikiSpacesByRoute(request, route);
route = '';
});

test('menu item opens the source file in GitHub editor', async ({
page,
request,
}) => {
route = `git-sync-edit-${Date.now()}`;
const space = await createDoc<WikiSpace & { root_group: string }>(
request,
'Wiki Space',
{
route,
space_name: route,
is_published: true,
git_synced: 1,
repo_full_name: REPO,
branch: BRANCH,
last_sync_status: 'Success',
last_sync_time: '2026-01-01 00:00:00',
},
);

// A nested leaf page with a repo-relative source_path.
const leafSourcePath = 'docs/guides/setup.md';
const leafTitle = `Setup ${Date.now()}`;
await createDoc<WikiDocument>(request, 'Wiki Document', {
title: leafTitle,
route: `${route}/setup`,
content: '# Setup\n\nFrom the repo.',
wiki_space: space.name,
parent_wiki_document: space.root_group,
is_published: true,
source_path: leafSourcePath,
});

await page.goto(`/wiki/spaces/${space.name}`);
await page.waitForLoadState('networkidle');

// Open the synced page.
await page.locator('aside').getByText(leafTitle, { exact: true }).click();
await page.waitForURL(/\/page\//);
await expect(page.locator('.ProseMirror')).toBeVisible({ timeout: 10000 });

// Stub window.open so we can read the URL without navigating away.
await page.evaluate(() => {
// @ts-expect-error test-only hook
window.__openedUrl = null;
window.open = (url) => {
// @ts-expect-error test-only hook
window.__openedUrl = url;
return null;
};
});

await page.getByRole('button', { name: 'More actions' }).click();
const editItem = page.getByRole('menuitem', { name: 'Edit on GitHub' });
await expect(editItem).toBeVisible();
await editItem.click();

const opened = await page.evaluate(
// @ts-expect-error test-only hook
() => window.__openedUrl,
);
expect(opened).toBe(
`https://github.com/${REPO}/edit/${BRANCH}/${leafSourcePath}`,
);
});
});
86 changes: 86 additions & 0 deletions e2e/tests/git-sync-readonly.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { expect, test } from '@playwright/test';
import { createDoc } from '../helpers/frappe';
import {
type WikiSpace,
cleanupWikiSpacesByRoute,
createTestWikiDocument,
} from '../helpers/wiki';

/**
* TB1b-ii — a git-synced Wiki Space renders read-only in the authoring SPA.
*
* We seed a synced space + a published page directly via the API (bypassing the
* GitHub fetch) and set last_sync_time so SpaceDetails does not auto-kick a real
* sync. The SPA should then show the "Synced from GitHub" banner, source the
* sidebar from the live tree, hide every mutation affordance, and open the page
* in a non-editable viewer.
*/
test.describe('Git-synced space (read-only)', () => {
const REPO = 'frappe/wiki';
const BRANCH = 'main';

let route: string;

test.afterEach(async ({ request }) => {
if (route) await cleanupWikiSpacesByRoute(request, route);
route = '';
});

test('renders read-only with no editing affordances', async ({
page,
request,
}) => {
route = `git-sync-ro-${Date.now()}`;
// last_sync_time is set so SpaceDetails treats the space as already
// synced and skips the auto initial-sync (which would hit GitHub).
const space = await createDoc<WikiSpace & { root_group: string }>(
request,
'Wiki Space',
{
route,
space_name: route,
is_published: true,
git_synced: 1,
repo_full_name: REPO,
branch: BRANCH,
last_sync_status: 'Success',
last_sync_time: '2026-01-01 00:00:00',
},
);

const pageTitle = `Synced Page ${Date.now()}`;
await createTestWikiDocument(request, {
title: pageTitle,
content: '# Synced Heading\n\nThis content comes from the repo.',
wiki_space: space.name,
parent_wiki_document: space.root_group,
is_published: true,
});

await page.goto(`/wiki/spaces/${space.name}`);
await page.waitForLoadState('networkidle');

// Synced banner: repo link (title) + "Synced from GitHub" subtitle + Sync now.
await expect(
page.locator(`a[href="https://github.com/${REPO}"]`),
).toBeVisible({ timeout: 10000 });
await expect(page.getByText('Synced from GitHub')).toBeVisible();
await expect(page.getByRole('button', { name: 'Sync now' })).toBeVisible();

// No create / mutation affordances in the sidebar.
await expect(page.locator('button[title="New Page"]')).toHaveCount(0);
await expect(page.locator('button[title="New Group"]')).toHaveCount(0);

// Open the synced page and confirm the viewer is non-editable.
await page.locator('aside').getByText(pageTitle, { exact: true }).click();
await page.waitForURL(/\/page\//);

const editor = page.locator('.ProseMirror');
await expect(editor).toBeVisible({ timeout: 10000 });
await expect(editor).toHaveAttribute('contenteditable', 'false');

// No Save button and no editor toolbar in read-only mode.
await expect(page.getByRole('button', { name: 'Save' })).toHaveCount(0);
await expect(page.locator('.wiki-editor-container button')).toHaveCount(0);
});
});
Loading
Loading