Skip to content

feat: add agent-authored guides and simplify the dashboard - #107

Merged
StanGirard merged 7 commits into
mainfrom
feat/finish-captuto-mvp
Sep 9, 2026
Merged

StanGirard merged 7 commits into
mainfrom
feat/finish-captuto-mvp

Conversation

@StanGirard

@StanGirard StanGirard commented May 13, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Captuto can turn a human Mac recording into an agent-authored guide. The recorder offers browser sign-in when no credential is configured, then retrieves its own token automatically. A standard HTTP MCP exposes captures, narration/transcription, step editing, annotations, rendered previews, sharing and PDF export; it does not control the recorded application.

The authenticated homepage now has working search, sorting and status filters, 25-guide server pagination, clear errors and real edit/share/PDF actions. Placeholder analytics, notifications, folders and browser recording controls are removed. A local 1,001-guide workload reduced initial JSON from 221,180 to 5,138 bytes (97.7%); warm API latency was similar (64 vs 61 ms). Initial dashboard JavaScript decreased from 152 to 147 kB. Details and reproducible checks: docs/captuto/dashboard-performance.md.

This branch integrates the current Studio editor from main, adds rectangle tools and reliable save errors, and refreshes the public guide and PDF layout. It also retains the existing billing work from this PR. The web redesign is partial; this is not a claim that the entire product has been redesigned or launch-tested.

Verification

  • Workspace lint, TypeScript, unit tests and production web/extension builds.
  • Local database migrations, desktop-pairing concurrency/revocation checks, and transactional dashboard ownership/pagination/count/search assertions.
  • agent-browser: Studio edit/preview, settings, dashboard, public desktop/mobile guide.
  • Actual HTTP MCP replay: audio upload, idempotent ingestion, 10 tools, annotation correction and preview, invalid geometry rejection, PDF and sharing.
  • Native Mac sign-in was exercised against the local browser without copying a key.

Independent review-code-dev deep review covered 160 files with frontend, auth, API, export-privacy and migration lenses. Its two findings were fixed and rechecked: idempotent Stripe checkout and bounded pairing admission. The dashboard delta received a targeted follow-up review; its 320px header overflow was fixed and rechecked. No issues remain in the review gate. Local checks pass (365 web tests, 71 extension tests, 21 Swift tests); all five executed latest-SHA checks pass. GitHub also retains eleven external-app suites in queued state with zero runs (also present on the previous commit); strict suite-level readiness awaits those integrations, so this is not an all-suites-green claim.

Release notes

Configure server-side SUPABASE_SERVICE_ROLE_KEY before code/migration rollout. Public policy removal requires the matching server routes; deploy through the existing CI/CD. Recovery/deploy-order notes: docs/captuto/development.md. Deepgram is required for transcription. Existing Stripe billing needs STRIPE_SECRET_KEY, STRIPE_PRICE_ID, and STRIPE_WEBHOOK_SECRET outside Git.

A long real-world manual recording and performance acceptance are still product validation work. Audio uploads remain subject to hosting request-size limits. No manual deployment or merge is part of this PR preparation.

Screenshots

Landing

Landing

Dashboard

Dashboard

Mobile dashboard

Mobile dashboard

Settings and MCP

Settings and MCP

Studio editor

Studio editor

Guide preview

Guide preview

Public guide

Public guide

Mobile guide

Mobile guide

PDF export

PDF export

Mac recorder

Mac recorder

Mac connection

Mac connection

Connected recorder

Connected recorder

Browser authorization

Browser authorization

No Linear issue: work was explicitly authorized without a ticket.

@supabase

supabase Bot commented May 13, 2026 •

Copy link
Copy Markdown

Updates to Preview Branch (feat/finish-captuto-mvp) ↗︎

Deployments Status Updated
Database ✅ Tue, 08 Sep 2026 21:35:56 UTC
Services ✅ Tue, 08 Sep 2026 21:35:56 UTC
APIs ✅ Tue, 08 Sep 2026 21:35:56 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations ✅ Tue, 08 Sep 2026 21:35:57 UTC
Migrations ✅ Tue, 08 Sep 2026 21:35:58 UTC
Seeding ✅ Tue, 08 Sep 2026 21:36:00 UTC
Edge Functions ✅ Tue, 08 Sep 2026 21:36:00 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

5 issues found across 42 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="apps/web/components/editor/EditorClient.tsx">

<violation number="1" location="apps/web/components/editor/EditorClient.tsx:71">
P2: Title-save failures set `saveStatus` to `error`, but successful title saves never reset it, so the editor can show a stale error state.</violation>

<violation number="2" location="apps/web/components/editor/EditorClient.tsx:249">
P2: New operation-specific errors can become stale because success paths for those operations don’t clear `editorError`.</violation>
</file>

<file name="apps/web/app/login/page.tsx">

<violation number="1" location="apps/web/app/login/page.tsx:26">
P2: `Promise.race` with a timeout can misclassify slow authenticated sessions as logged out, leaving signed-in users on the login page.</violation>
</file>

<file name="apps/web/app/dashboard/page.tsx">

<violation number="1" location="apps/web/app/dashboard/page.tsx:64">
P2: Using `mutateAsync` here introduces an unhandled rejection path on delete failures. Use `mutate` (or catch the promise) so errors are handled via mutation state without bubbling a rejected promise from the click handler.</violation>
</file>

<file name="apps/web/app/api/oembed/route.ts">

<violation number="1" location="apps/web/app/api/oembed/route.ts:57">
P2: Using expiring signed screenshot URLs in `thumbnail_url` can produce broken oEmbed thumbnails after the 7-day signature expires.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

console.error('Failed to update title:', error);
// Rollback on error
setEditorError('The title could not be saved. Your previous title was restored.');
setSaveStatus('error');

@cubic-dev-ai cubic-dev-ai Bot May 13, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Title-save failures set saveStatus to error, but successful title saves never reset it, so the editor can show a stale error state.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/components/editor/EditorClient.tsx, line 71:

<comment>Title-save failures set `saveStatus` to `error`, but successful title saves never reset it, so the editor can show a stale error state.</comment>

<file context>
@@ -63,9 +64,11 @@ export function EditorClient({
       console.error('Failed to update title:', error);
-      // Rollback on error
+      setEditorError('The title could not be saved. Your previous title was restored.');
+      setSaveStatus('error');
       setTutorial((prev) => ({ ...prev, title: previousTitle }));
     }
</file context>
Fix with Cubic

}
} catch (error) {
console.error('Failed to reorder steps:', error);
setEditorError('The step order could not be saved. The previous order was restored.');

@cubic-dev-ai cubic-dev-ai Bot May 13, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: New operation-specific errors can become stale because success paths for those operations don’t clear editorError.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/components/editor/EditorClient.tsx, line 249:

<comment>New operation-specific errors can become stale because success paths for those operations don’t clear `editorError`.</comment>

<file context>
@@ -241,6 +246,7 @@ export function EditorClient({
       }
     } catch (error) {
       console.error('Failed to reorder steps:', error);
+      setEditorError('The step order could not be saved. The previous order was restored.');
       setSteps(previousSteps);
     } finally {
</file context>
Fix with Cubic

const checkAuth = async () => {
try {
const supabase = createClient();
const authResult = await Promise.race([

@cubic-dev-ai cubic-dev-ai Bot May 13, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Promise.race with a timeout can misclassify slow authenticated sessions as logged out, leaving signed-in users on the login page.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/login/page.tsx, line 26:

<comment>`Promise.race` with a timeout can misclassify slow authenticated sessions as logged out, leaving signed-in users on the login page.</comment>

<file context>
@@ -16,18 +18,37 @@ export default function LoginPage() {
+    const checkAuth = async () => {
+      try {
+        const supabase = createClient();
+        const authResult = await Promise.race([
+          supabase.auth.getUser(),
+          new Promise<null>((resolve) =>
</file context>
Fix with Cubic

Comment thread apps/web/app/dashboard/page.tsx Outdated
Comment on lines +64 to +65
const handleDelete = async (tutorialId: string) => {
await deleteMutation.mutateAsync(tutorialId);

@cubic-dev-ai cubic-dev-ai Bot May 13, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Using mutateAsync here introduces an unhandled rejection path on delete failures. Use mutate (or catch the promise) so errors are handled via mutation state without bubbling a rejected promise from the click handler.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/dashboard/page.tsx, line 64:

<comment>Using `mutateAsync` here introduces an unhandled rejection path on delete failures. Use `mutate` (or catch the promise) so errors are handled via mutation state without bubbling a rejected promise from the click handler.</comment>

<file context>
@@ -49,22 +51,18 @@ export default function DashboardPage() {
 
-  const handleDelete = (tutorialId: string) => {
-    deleteMutation.mutate(tutorialId);
+  const handleDelete = async (tutorialId: string) => {
+    await deleteMutation.mutateAsync(tutorialId);
   };
</file context>
Suggested change
const handleDelete = async (tutorialId: string) => {
await deleteMutation.mutateAsync(tutorialId);
const handleDelete = (tutorialId: string) => {
deleteMutation.mutate(tutorialId);
Fix with Cubic

Comment on lines +57 to +59
const thumbnailUrl =
previewImageUrl ||
`${baseUrl}/api/og/tutorial?title=${encodeURIComponent(tutorial.title)}&steps=${steps.length}`;

@cubic-dev-ai cubic-dev-ai Bot May 13, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Using expiring signed screenshot URLs in thumbnail_url can produce broken oEmbed thumbnails after the 7-day signature expires.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/web/app/api/oembed/route.ts, line 57:

<comment>Using expiring signed screenshot URLs in `thumbnail_url` can produce broken oEmbed thumbnails after the 7-day signature expires.</comment>

<file context>
@@ -36,45 +36,45 @@ export async function GET(request: NextRequest) {
-  const embedToken = tutorial.public_token;
+  const embedToken = tutorial.publicToken;
   const embedUrl = `${baseUrl}/t/${embedToken}/embed`;
+  const thumbnailUrl =
+    previewImageUrl ||
+    `${baseUrl}/api/og/tutorial?title=${encodeURIComponent(tutorial.title)}&steps=${steps.length}`;
</file context>
Suggested change
const thumbnailUrl =
previewImageUrl ||
`${baseUrl}/api/og/tutorial?title=${encodeURIComponent(tutorial.title)}&steps=${steps.length}`;
const thumbnailUrl = `${baseUrl}/api/og/tutorial?title=${encodeURIComponent(tutorial.title)}&steps=${steps.length}`;
Fix with Cubic

@StanGirard StanGirard changed the title feat(web): finish captuto mvp feat: add agent-authored guides and connected Mac recording Sep 8, 2026
@StanGirard StanGirard changed the title feat: add agent-authored guides and connected Mac recording feat: add agent-authored guides and simplify the dashboard Sep 8, 2026
@StanGirard
StanGirard merged commit 12f0343 into main Sep 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant