Skip to content

fix: remote spaces, content browsing, and docs as real remote space (#66, #68)#67

Open
nsheaps wants to merge 13 commits intomainfrom
claude/fix-remote-spaces-browsing-svBxQ
Open

fix: remote spaces, content browsing, and docs as real remote space (#66, #68)#67
nsheaps wants to merge 13 commits intomainfrom
claude/fix-remote-spaces-browsing-svBxQ

Conversation

@nsheaps
Copy link
Copy Markdown
Owner

@nsheaps nsheaps commented Mar 23, 2026

Summary

This PR addresses multiple issues with remote spaces and documentation:

Remote Spaces Browsing (#66)

  • Fix space details action buttons to display inline
  • Add GitHub icon button and page menu for remote repo pages
  • Compute page count and storage stats for inactive spaces

Docs as Real Remote Space (#68)

  • Removed the entire separate docs code path (activeSpace === 'docs', docs-specific sidebar, banner, content rendering)
  • Docs is now a real remote space in the manifest, using the exact same infrastructure as any other cloned repo
  • ensureDocsSpace() creates a remote space entry for github.com/nsheaps/cept (scoped to docs/content/) on app startup
  • Clones from GitHub via cloneRemoteRepo or falls back to bundled DOCS_PAGES/DOCS_CONTENT
  • Uses __HEAD_BRANCH__ build-time global so preview deploys clone from the PR branch
  • Docs now gets all the same UI features as any remote space:
    • Top nav buttons (GitHub link, page menu)
    • Settings panel with Remote URL, Branch, Last synced
    • Refresh button to re-clone from remote
    • Standard sidebar with space switcher
  • Auto-created on startup so docs always appears in space list
  • Removed cept-docs exclusions from SettingsModal refresh/browse buttons

Editor Read-Only for Remote Spaces

  • Editor is now non-editable (contenteditable="false") when viewing a remote space
  • Inline toolbar hidden in read-only mode
  • Prevents accidental edits to cloned content (docs and other remote repos)

Partial-SubPath URL Resolution

  • Fix resolveRouteToSpace to match directory URLs (e.g., /g/repo/blob/main/docs/) to existing spaces with more-specific subPaths (e.g., docs/content)
  • Uses path-boundary matching so docs matches docs/content but doc does not
  • Prevents duplicate spaces from being auto-created when navigating to a parent directory URL
  • Eliminates redundant "Cloning..." flash on reload when the space already exists
  • Fixes wrong content (SPECIFICATION.md) appearing when the auto-clone used subPath docs instead of docs/content

Closes #66
Closes #68

Test plan

  • bun run validate passes (lint + typecheck + 1743 tests across 101 files)
  • Fresh load — verify docs space appears in space switcher
  • Navigate to /g/github.com/nsheaps/cept/blob/main/docs/ — verify it resolves to the existing docs space (not creating a duplicate)
  • No "Cloning..." flash on reload when docs space already exists
  • Check settings panel — verify only one docs space, not duplicates
  • Verify editor is read-only when viewing a remote space
  • Verify editor is editable when viewing a local space
  • Preview deploy — verify docs clone from PR branch

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD

@nsheaps nsheaps self-assigned this Mar 23, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 23, 2026

Release Version Check

🟡 MINOR version bump

Version
Current 0.7.0
Next 0.8.0

This version will be released automatically when this PR is merged to main.

Changelog preview

0.8.0 (2026-03-25)

Features

  • load docs space from remote Git with bundled fallback (481962c), closes #68

Bug Fixes

  • add GitHub icon button and page menu for remote repo pages, closes #63 (a7f452c)
  • address code review findings (d782e70)
  • auto-create docs space on startup and use PR branch for previews (8012557)
  • compute page count and storage for inactive spaces, closes #59 (6bb5ded)
  • file browser uses space-specific root path, closes #60 (6e7a9eb)
  • handle branch names with / and add 404 page, closes #65 (0ba2d07)
  • make editor read-only for remote spaces (7af02ad)
  • make space details action buttons inline, closes #61 (f19fd60)
  • resolve partial-subpath URLs to existing spaces (d497221)
  • resolve URL path encoding for remote repo pages, closes #64 (b5544be)
  • sidebar nav, folder structure, and .cept.yaml config, closes #62 (d12b829)

Refactoring

  • make docs a real remote space instead of special case (9ca0e69), closes #68

@github-actions
Copy link
Copy Markdown
Contributor

Preview Deployment

The web app for this PR has been deployed:

Open Preview

Use this to verify the app works correctly, especially for dependency updates.

Copy link
Copy Markdown
Owner Author

@nsheaps nsheaps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review Summary

Self-review performed with 8 parallel review agents. Issues found were fixed in commit 14c095b.

Category Initial Post-Fix Status
Simplicity 58 ~75 ⚠️ Improved: removed scoring algorithm, extracted GitHubIcon, IIFE→useMemo
Flexibility 62 ~78 ⚠️ Improved: simplified resolveRouteToSpace, fixed notFound persistence
Usability 78 ~85 ✅ Fixed: onGoBack as prop, history check, notFound clears on navigation
Documentation 72 ~78 ⚠️ JSDoc improved; .cept.yaml user docs deferred
Security 72 ~88 ✅ Fixed: GitHub URL domain validation against allowlist
Patterns 72 ~80 ⚠️ Improved: no direct window access in NotFoundPage
Best Practices 62 ~82 ⚠️ Fixed: memory leak, added NotFoundPage tests, extractFrontMatter tests
Quality 72 ~82 ⚠️ Fixed: test coverage gaps, pageIdToFilename consistency
Issues fixed in review commit
  1. Memory leak: Added cancellation flag to inactive space stats useEffect
  2. notFound persistence: Cleared on popstate navigation
  3. Open redirect: GitHub URL validated against KNOWN_GIT_HOSTS allowlist
  4. SVG duplication: Extracted GitHubIcon component
  5. IIFE in JSX: Replaced with useMemo for currentGithubUrl
  6. Complex scoring: Simplified resolveRouteToSpace to longest-prefix match
  7. Redundant branches: Fixed if (exact) / else returning same value
  8. Direct window access: NotFoundPage.onGoBack is now an optional callback prop
  9. Extension mismatch: pageIdToFilename aligned with router's FILE_EXTENSIONS
  10. Missing tests: Added 8 NotFoundPage tests + 4 extractFrontMatter tests
Known limitations (deferred)
  • Custom YAML parser only handles hide: — future .cept.yaml keys will need parser updates
  • No page ID migration from old git-{flattened} format — users must re-clone
  • Inactive space storage size shows 0 (would require reading all files; page count is shown)
  • First-time visitors with multi-segment branch URLs to un-cloned repos get router's best guess

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD

@nsheaps nsheaps marked this pull request as ready for review March 23, 2026 04:28
github-actions Bot added a commit that referenced this pull request Mar 23, 2026
@nsheaps nsheaps changed the title fix: remote spaces & content browsing (#66) fix: remote spaces, content browsing, and remote docs loading (#66, #68) Mar 23, 2026
@nsheaps nsheaps changed the title fix: remote spaces, content browsing, and remote docs loading (#66, #68) fix: remote spaces, content browsing, and docs as real remote space (#66, #68) Mar 23, 2026
github-actions Bot added a commit that referenced this pull request Mar 23, 2026
github-actions Bot added a commit that referenced this pull request Mar 23, 2026
github-actions Bot added a commit that referenced this pull request Mar 23, 2026
github-actions Bot added a commit that referenced this pull request Mar 23, 2026
claude added 13 commits March 25, 2026 01:33
Page IDs now use actual file paths (e.g., "docs/getting-started.md")
instead of flattened dash-encoded slugs (e.g., "git-docs-getting-started-md").
This prevents collisions between files like foo/bar.md and foo-bar.md.

- Router uses file extension detection instead of git- prefix
- Storage functions handle page IDs with .md extension
- Added resolveRouteToSpace for matching minimal route spaceIds to
  configured spaces with subPath
- Added collision regression test

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
- Space IDs now use :: separator between branch and subPath
  (e.g., repo@claude/fix::docs) to avoid ambiguity with / in branches
- resolveRouteToSpace tries progressive branch matching to handle
  multi-segment branch names like "claude/setup-fix"
- Added NotFoundPage component for clear 404 errors with navigation
- Clone failures now show proper 404 page instead of error string
- Legacy / separator still supported for backward compatibility

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
The file browser was always browsing from the filesystem root (/),
showing the demo space's files regardless of which space was selected.
Now it uses the space-specific storage path (.cept/spaces/{spaceId})
so each space browses its own files.

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
- README.md/index.md files in folders now serve as the folder's page
  content instead of creating separate pages
- Frontmatter title field is used for page titles when available
- Added .cept.yaml folder-level config support with hide: [] to exclude
  files/folders from the sidebar (applies to folder and subfolders)
- parseCeptYaml supports both inline [a, b] and block - a list syntax
- extractFrontMatter reads YAML front matter for title/metadata

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Inactive spaces now load their stats asynchronously from the backend
instead of always showing 0 for pages and storage used. The active
space still uses live React state for accurate real-time counts.

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
#63

- GitHub icon button appears next to the triple-dot menu on remote pages
- Menu includes "View page on GitHub" link and "Share (coming soon)" item
- GitHub URL computed from space metadata (repo, branch, subPath)
- Also works for docs pages via getDocsSourceUrl
- Removed the source callout banner from docs pages (replaced by menu)

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Action buttons (Refresh, Browse, Delete) in space details now display
horizontally in a flex row instead of stacking vertically. Labels
shortened to icon + first word for a cleaner, more compact UI.

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
- Fix memory leak in inactive space stats useEffect (add cancellation)
- Clear notFound state on popstate (back/forward navigation)
- Validate GitHub URL domain against known hosts (prevent open redirect)
- Extract GitHubIcon component to remove SVG duplication
- Replace IIFE with useMemo for currentGithubUrl
- Simplify resolveRouteToSpace (longest-prefix match, no scoring)
- Fix redundant if/else branches in resolveRouteToSpace
- Make onGoBack optional prop in NotFoundPage (no direct window access)
- Align pageIdToFilename extensions with router's FILE_EXTENSIONS
- Add NotFoundPage component tests (8 test cases)
- Add extractFrontMatter tests (4 test cases)

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Add docs-loader module that clones github.com/nsheaps/cept scoped to
docs/content/ using the existing cloneRemoteRepo infrastructure.
Cloned content is cached in .cept/docs-cache/ for instant subsequent loads.
When cloning fails (offline, CORS errors), falls back to the bundled
DOCS_PAGES/DOCS_CONTENT constants.

- New docs-loader.ts with loadDocs() and getRemoteDocsSourceUrl()
- App.tsx uses loader on first docs space open
- Sidebar reflects actual repository layout when loaded from remote
- View on GitHub links work with both remote file paths and bundled IDs
- Loading state shown while clone is in progress
- Banner indicates content source (remote, cached, or bundled fallback)

Closes #68

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Remove the entire separate docs rendering path (activeSpace === 'docs',
docs-specific sidebar, banner, content rendering) and make docs a normal
remote space in the manifest.

handleOpenDocs now:
1. Calls ensureDocsSpace() which creates a remote space entry for
   github.com/nsheaps/cept (docs/content subpath) if it doesn't exist
2. Clones from GitHub or falls back to bundled DOCS_PAGES/DOCS_CONTENT
3. Switches to the space via the standard loadAndApplySpaceState flow

This means docs now gets the same UI as any other remote space:
- Top nav buttons (GitHub link, page menu)
- Settings panel with Remote URL, Branch, Last synced
- Refresh button to re-clone from remote
- Standard sidebar with space switcher

Removed:
- activeSpace state and all docs/user branching
- docsPages, docsContents, docsSource, docsLoading state
- handleDocsPageSelect, handleDocsPageToggle callbacks
- Docs-specific sidebar with read-only handlers
- Docs-specific content area with banner and CeptEditor
- DOCS_SPACE_INFO constant usage
- 'cept-docs' exclusion from SettingsModal refresh button

Closes #68

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Two issues after converting docs to a real remote space:

1. After data reset, docs space didn't appear in space list because it
   was only created on-demand when clicking "Help & Docs". Now
   ensureDocsSpace() runs during app initialization so the docs space
   is always present in the space switcher.

2. Preview deploys hardcoded DOCS_BRANCH='main' instead of using the
   __HEAD_BRANCH__ build-time global. Now docs-loader uses the same
   pattern as docs-content.ts to pick up the PR branch for previews.

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
Remote spaces (including docs) are cloned from Git repos and should not
be editable. Pass editable={false} to CeptEditor when the active space
is a remote space, which sets contenteditable="false" on the ProseMirror
element and hides the inline toolbar.

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
When navigating to a URL like /g/repo/blob/main/docs/, the router
produces a spaceId with subPath "docs" and no pageId. Previously,
resolveRouteToSpace returned this as-is without searching for matching
spaces, causing a redundant clone with subPath "docs" (which includes
SPECIFICATION.md and other non-content files) even when a space with
subPath "docs/content" already existed.

Now resolveRouteToSpace searches for spaces whose subPath starts with
the route's subPath on a path boundary. This prevents:
- Duplicate spaces for the same repo/branch with different subPath depths
- "Cloning..." flash on reload when the space already exists
- Wrong content (SPECIFICATION.md) appearing instead of docs content

https://claude.ai/code/session_01TF6rYpGkqBWjzNQHgqrVmD
@nsheaps nsheaps force-pushed the claude/fix-remote-spaces-browsing-svBxQ branch from fb17292 to d497221 Compare March 25, 2026 01:35
github-actions Bot added a commit that referenced this pull request Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants