jettypod 4.4.118 → 4.4.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env +4 -3
- package/Cargo.lock +6450 -0
- package/Cargo.toml +35 -0
- package/README.md +5 -1
- package/TAURI-MIGRATION-PLAN.md +840 -0
- package/apps/dashboard/app/connect-claude/page.tsx +5 -6
- package/apps/dashboard/app/decision/[id]/page.tsx +63 -58
- package/apps/dashboard/app/demo/gates/page.tsx +43 -45
- package/apps/dashboard/app/design-system/page.tsx +868 -0
- package/apps/dashboard/app/globals.css +80 -4
- package/apps/dashboard/app/install-claude/page.tsx +4 -6
- package/apps/dashboard/app/login/page.tsx +72 -54
- package/apps/dashboard/app/page.tsx +101 -48
- package/apps/dashboard/app/settings/page.tsx +61 -13
- package/apps/dashboard/app/signup/page.tsx +242 -0
- package/apps/dashboard/app/subscribe/page.tsx +0 -2
- package/apps/dashboard/app/tests/page.tsx +37 -4
- package/apps/dashboard/app/welcome/page.tsx +13 -16
- package/apps/dashboard/app/work/[id]/page.tsx +117 -118
- package/apps/dashboard/app/work/[id]/proof/page.tsx +1489 -0
- package/apps/dashboard/components/AppShell.tsx +92 -85
- package/apps/dashboard/components/CardMenu.tsx +45 -12
- package/apps/dashboard/components/ClaudePanel.tsx +771 -850
- package/apps/dashboard/components/ClaudePanelInput.tsx +43 -15
- package/apps/dashboard/components/ConnectClaudeScreen.tsx +17 -34
- package/apps/dashboard/components/CopyableId.tsx +3 -4
- package/apps/dashboard/components/DetailReviewActions.tsx +100 -0
- package/apps/dashboard/components/DragContext.tsx +134 -63
- package/apps/dashboard/components/DraggableCard.tsx +3 -5
- package/apps/dashboard/components/DropZone.tsx +6 -7
- package/apps/dashboard/components/EditableDetailDescription.tsx +7 -13
- package/apps/dashboard/components/EditableDetailTitle.tsx +6 -13
- package/apps/dashboard/components/EditableTitle.tsx +26 -7
- package/apps/dashboard/components/ElapsedTimer.tsx +66 -0
- package/apps/dashboard/components/EpicGroup.tsx +359 -0
- package/apps/dashboard/components/GateCard.tsx +79 -17
- package/apps/dashboard/components/GateChoiceCard.tsx +15 -18
- package/apps/dashboard/components/InstallClaudeScreen.tsx +15 -32
- package/apps/dashboard/components/JettyLoader.tsx +37 -0
- package/apps/dashboard/components/KanbanBoard.tsx +368 -958
- package/apps/dashboard/components/KanbanCard.tsx +740 -0
- package/apps/dashboard/components/LazyCard.tsx +62 -0
- package/apps/dashboard/components/LazyMarkdown.tsx +11 -0
- package/apps/dashboard/components/MainNav.tsx +38 -73
- package/apps/dashboard/components/MessageBlock.tsx +468 -0
- package/apps/dashboard/components/ModeStartCard.tsx +15 -16
- package/apps/dashboard/components/OnboardingWelcome.tsx +213 -0
- package/apps/dashboard/components/PlaceholderCard.tsx +3 -4
- package/apps/dashboard/components/ProjectSwitcher.tsx +30 -30
- package/apps/dashboard/components/PrototypeTimeline.tsx +72 -51
- package/apps/dashboard/components/RealTimeKanbanWrapper.tsx +406 -388
- package/apps/dashboard/components/RealTimeTestsWrapper.tsx +373 -235
- package/apps/dashboard/components/ReviewFooter.tsx +139 -0
- package/apps/dashboard/components/SessionList.tsx +19 -19
- package/apps/dashboard/components/SubscribeContent.tsx +91 -47
- package/apps/dashboard/components/TestTree.tsx +16 -16
- package/apps/dashboard/components/TipCard.tsx +16 -17
- package/apps/dashboard/components/Toast.tsx +5 -6
- package/apps/dashboard/components/TypeIcon.tsx +55 -0
- package/apps/dashboard/components/ViewModeToolbar.tsx +104 -0
- package/apps/dashboard/components/WaveCompletionAnimation.tsx +52 -65
- package/apps/dashboard/components/WelcomeScreen.tsx +19 -35
- package/apps/dashboard/components/WorkItemHeader.tsx +4 -5
- package/apps/dashboard/components/WorkItemTree.tsx +11 -32
- package/apps/dashboard/components/settings/AccountSection.tsx +55 -35
- package/apps/dashboard/components/settings/AiContextSection.tsx +89 -0
- package/apps/dashboard/components/settings/ContextDocumentsSection.tsx +317 -0
- package/apps/dashboard/components/settings/EnvVarsSection.tsx +74 -152
- package/apps/dashboard/components/settings/GeneralSection.tsx +162 -56
- package/apps/dashboard/components/settings/ProjectStackSection.tsx +948 -0
- package/apps/dashboard/components/settings/SettingsLayout.tsx +4 -5
- package/apps/dashboard/components/ui/Button.tsx +104 -0
- package/apps/dashboard/components/ui/Input.tsx +78 -0
- package/apps/dashboard/components.json +1 -1
- package/apps/dashboard/contexts/ClaudeSessionContext.tsx +711 -418
- package/apps/dashboard/contexts/ConnectionStatusContext.tsx +25 -5
- package/apps/dashboard/contexts/UsageContext.tsx +87 -32
- package/apps/dashboard/dev.sh +35 -0
- package/apps/dashboard/eslint.config.mjs +9 -9
- package/apps/dashboard/hooks/useKanbanAnimation.ts +29 -0
- package/apps/dashboard/hooks/useKanbanUndo.ts +83 -0
- package/apps/dashboard/hooks/useWebSocket.ts +138 -83
- package/apps/dashboard/index.html +73 -0
- package/apps/dashboard/lib/constants.ts +43 -0
- package/apps/dashboard/lib/data-bridge.ts +722 -0
- package/apps/dashboard/lib/db.ts +69 -1265
- package/apps/dashboard/lib/environment-config.ts +173 -0
- package/apps/dashboard/lib/environment-verification.ts +119 -0
- package/apps/dashboard/lib/kanban-utils.ts +270 -0
- package/apps/dashboard/lib/proof-run.ts +495 -0
- package/apps/dashboard/lib/proof-scenario-runner.ts +346 -0
- package/apps/dashboard/lib/run-migrations.js +27 -2
- package/apps/dashboard/lib/service-recovery.ts +326 -0
- package/apps/dashboard/lib/session-state-machine.ts +1 -0
- package/apps/dashboard/lib/session-state-utils.ts +0 -164
- package/apps/dashboard/lib/session-stream-manager.ts +308 -134
- package/apps/dashboard/lib/shadows.ts +7 -0
- package/apps/dashboard/lib/stream-manager-registry.ts +46 -6
- package/apps/dashboard/lib/tauri-bridge.ts +102 -0
- package/apps/dashboard/lib/tauri.ts +106 -0
- package/apps/dashboard/lib/utils.ts +6 -0
- package/apps/dashboard/next-env.d.ts +1 -1
- package/apps/dashboard/package.json +21 -32
- package/apps/dashboard/public/bug-icon.png +0 -0
- package/apps/dashboard/public/buoy-icon.png +0 -0
- package/apps/dashboard/public/fonts/Satoshi-Variable.woff2 +0 -0
- package/apps/dashboard/public/fonts/Satoshi-VariableItalic.woff2 +0 -0
- package/apps/dashboard/public/in-flight-seagull.png +0 -0
- package/apps/dashboard/public/jetty-icon-loading-alt.svg +11 -0
- package/apps/dashboard/public/jetty-icon-loading.svg +11 -0
- package/apps/dashboard/public/jettypod_logo.png +0 -0
- package/apps/dashboard/public/pier-icon.png +0 -0
- package/apps/dashboard/public/star-icon.png +0 -0
- package/apps/dashboard/public/wrench-icon.png +0 -0
- package/apps/dashboard/scripts/tauri-build.js +228 -0
- package/apps/dashboard/scripts/upload-tauri-to-r2.js +125 -0
- package/apps/dashboard/scripts/ws-server.js +191 -0
- package/apps/dashboard/src/main.tsx +12 -0
- package/apps/dashboard/src/router.tsx +107 -0
- package/apps/dashboard/src/vite-env.d.ts +1 -0
- package/apps/dashboard/tsconfig.json +7 -12
- package/apps/dashboard/tsconfig.tsbuildinfo +1 -1
- package/apps/dashboard/vite.config.ts +33 -0
- package/apps/update-server/src/index.ts +228 -80
- package/claude-hooks/global-guardrails.js +14 -13
- package/crates/jettypod-cli/Cargo.toml +19 -0
- package/crates/jettypod-cli/src/commands.rs +1249 -0
- package/crates/jettypod-cli/src/main.rs +595 -0
- package/crates/jettypod-core/Cargo.toml +26 -0
- package/crates/jettypod-core/build.rs +98 -0
- package/crates/jettypod-core/migrations/V1__baseline.sql +197 -0
- package/crates/jettypod-core/migrations/V2__work_items_indexes.sql +6 -0
- package/crates/jettypod-core/migrations/V3__qa_steps.sql +2 -0
- package/crates/jettypod-core/src/auth.rs +294 -0
- package/crates/jettypod-core/src/config.rs +397 -0
- package/crates/jettypod-core/src/db/mod.rs +507 -0
- package/crates/jettypod-core/src/db/recovery.rs +114 -0
- package/crates/jettypod-core/src/db/startup.rs +101 -0
- package/crates/jettypod-core/src/db/validate.rs +149 -0
- package/crates/jettypod-core/src/error.rs +76 -0
- package/crates/jettypod-core/src/git.rs +458 -0
- package/crates/jettypod-core/src/lib.rs +20 -0
- package/crates/jettypod-core/src/sessions.rs +625 -0
- package/crates/jettypod-core/src/skills.rs +556 -0
- package/crates/jettypod-core/src/work.rs +1086 -0
- package/crates/jettypod-core/src/worktree.rs +628 -0
- package/crates/jettypod-core/src/ws.rs +767 -0
- package/cucumber-test.cjs +6 -0
- package/cucumber.js +9 -3
- package/docs/COMMAND_REFERENCE.md +34 -0
- package/hooks/post-checkout +32 -75
- package/hooks/post-merge +111 -10
- package/jest.setup.js +1 -0
- package/jettypod.js +145 -116
- package/lib/bdd-preflight.js +96 -0
- package/lib/chore-taxonomy.js +33 -10
- package/lib/database.js +36 -16
- package/lib/db-watcher.js +1 -1
- package/lib/git-hooks/pre-commit +1 -1
- package/lib/jettypod-backup.js +27 -4
- package/lib/merge-lock.js +111 -253
- package/lib/migrations/027-plan-at-creation-column.js +3 -1
- package/lib/migrations/029-remove-autoincrement.js +307 -0
- package/lib/migrations/029-rename-corrupted-to-cleaned.js +149 -0
- package/lib/migrations/030-rejection-round-columns.js +54 -0
- package/lib/migrations/031-session-isolation-index.js +17 -0
- package/lib/migrations/index.js +47 -4
- package/lib/schema.js +10 -5
- package/lib/seed-onboarding.js +1 -1
- package/lib/update-command/index.js +9 -175
- package/lib/work-commands/index.js +144 -19
- package/lib/work-tracking/index.js +148 -27
- package/lib/worktree-diagnostics.js +16 -16
- package/lib/worktree-facade.js +1 -1
- package/lib/worktree-manager.js +8 -8
- package/lib/worktree-reconciler.js +5 -5
- package/package.json +9 -2
- package/scripts/ndjson-to-cucumber-json.js +152 -0
- package/scripts/postinstall.js +25 -0
- package/skills-templates/bug-mode/SKILL.md +79 -20
- package/skills-templates/bug-planning/SKILL.md +25 -29
- package/skills-templates/chore-mode/SKILL.md +171 -69
- package/skills-templates/chore-mode/verification.js +51 -10
- package/skills-templates/chore-planning/SKILL.md +47 -18
- package/skills-templates/design-system-selection/SKILL.md +273 -0
- package/skills-templates/epic-planning/SKILL.md +82 -48
- package/skills-templates/external-transition/SKILL.md +47 -47
- package/skills-templates/feature-planning/SKILL.md +173 -74
- package/skills-templates/production-mode/SKILL.md +69 -49
- package/skills-templates/request-routing/SKILL.md +4 -4
- package/skills-templates/simple-improvement/SKILL.md +74 -29
- package/skills-templates/speed-mode/SKILL.md +217 -141
- package/skills-templates/stable-mode/SKILL.md +148 -89
- package/apps/dashboard/README.md +0 -36
- package/apps/dashboard/app/api/claude/[workItemId]/message/route.ts +0 -386
- package/apps/dashboard/app/api/claude/[workItemId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/[workItemId]/route.ts +0 -167
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/content/route.ts +0 -52
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/message/route.ts +0 -378
- package/apps/dashboard/app/api/claude/sessions/[sessionId]/pin/route.ts +0 -24
- package/apps/dashboard/app/api/claude/sessions/cleanup/route.ts +0 -34
- package/apps/dashboard/app/api/claude/sessions/route.ts +0 -184
- package/apps/dashboard/app/api/decisions/[id]/route.ts +0 -25
- package/apps/dashboard/app/api/internal/set-project/route.ts +0 -17
- package/apps/dashboard/app/api/kanban/route.ts +0 -15
- package/apps/dashboard/app/api/settings/env-vars/route.ts +0 -125
- package/apps/dashboard/app/api/settings/general/route.ts +0 -21
- package/apps/dashboard/app/api/tests/route.ts +0 -9
- package/apps/dashboard/app/api/tests/run/route.ts +0 -82
- package/apps/dashboard/app/api/tests/run/stream/route.ts +0 -71
- package/apps/dashboard/app/api/tests/undefined/route.ts +0 -9
- package/apps/dashboard/app/api/usage/route.ts +0 -17
- package/apps/dashboard/app/api/work/[id]/description/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/epic/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/order/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/status/route.ts +0 -21
- package/apps/dashboard/app/api/work/[id]/title/route.ts +0 -21
- package/apps/dashboard/app/layout.tsx +0 -43
- package/apps/dashboard/components/UpgradeBanner.tsx +0 -29
- package/apps/dashboard/electron/ipc-handlers.js +0 -1028
- package/apps/dashboard/electron/main.js +0 -2124
- package/apps/dashboard/electron/preload.js +0 -123
- package/apps/dashboard/electron/session-manager.js +0 -141
- package/apps/dashboard/electron-builder.config.js +0 -357
- package/apps/dashboard/hooks/useClaudeSessions.ts +0 -299
- package/apps/dashboard/lib/claude-process-manager.ts +0 -492
- package/apps/dashboard/lib/db-bridge.ts +0 -282
- package/apps/dashboard/lib/prototypes.ts +0 -202
- package/apps/dashboard/lib/test-results-db.ts +0 -307
- package/apps/dashboard/lib/tests.ts +0 -282
- package/apps/dashboard/next.config.js +0 -50
- package/apps/dashboard/postcss.config.mjs +0 -7
- package/apps/dashboard/public/file.svg +0 -1
- package/apps/dashboard/public/globe.svg +0 -1
- package/apps/dashboard/public/next.svg +0 -1
- package/apps/dashboard/public/vercel.svg +0 -1
- package/apps/dashboard/public/window.svg +0 -1
- package/apps/dashboard/scripts/download-node.js +0 -104
- package/apps/dashboard/scripts/upload-to-r2.js +0 -89
- package/docs/bdd-guidance.md +0 -390
|
@@ -1,133 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { ClaudeSessionProvider, useClaudeSession } from '../contexts/ClaudeSessionContext';
|
|
6
|
-
import { ConnectionStatusProvider } from '../contexts/ConnectionStatusContext';
|
|
1
|
+
import { useEffect, useRef, useLayoutEffect } from 'react';
|
|
2
|
+
import { useLocation, useNavigate } from 'react-router-dom';
|
|
3
|
+
import { ClaudeSessionProvider, useSessionState, useSessionActions } from '../contexts/ClaudeSessionContext';
|
|
4
|
+
import { ConnectionStatusProvider, useConnectionStatus } from '../contexts/ConnectionStatusContext';
|
|
7
5
|
import { UsageProvider } from '../contexts/UsageContext';
|
|
8
6
|
import { ToastProvider } from './Toast';
|
|
9
7
|
import { MainNav } from './MainNav';
|
|
10
8
|
import { ClaudePanel } from './ClaudePanel';
|
|
9
|
+
import { LazyMotion, domAnimation, m } from 'framer-motion';
|
|
10
|
+
import { isTauri, auth } from '@/lib/tauri-bridge';
|
|
11
11
|
import type { ReactNode } from 'react';
|
|
12
12
|
|
|
13
|
+
// Module-level auth cache — auth status doesn't change within a session.
|
|
14
|
+
// Prevents redundant IPC calls if AppShell ever remounts.
|
|
15
|
+
let cachedAuthOk: boolean | null = null;
|
|
16
|
+
|
|
13
17
|
// Pages that should not show the nav header (pre-project screens)
|
|
14
|
-
const NO_NAV_PATHS = ['/login', '/subscribe', '/install-claude', '/connect-claude', '/welcome'];
|
|
18
|
+
const NO_NAV_PATHS = ['/login', '/signup', '/subscribe', '/install-claude', '/connect-claude', '/welcome', '/prototypes/onboarding'];
|
|
15
19
|
|
|
16
20
|
// Pages accessible without authentication
|
|
17
|
-
const PUBLIC_PATHS = ['/login', '/subscribe', '/install-claude', '/connect-claude', '/welcome'];
|
|
21
|
+
const PUBLIC_PATHS = ['/login', '/signup', '/subscribe', '/install-claude', '/connect-claude', '/welcome', '/design-system', '/prototypes/onboarding'];
|
|
18
22
|
|
|
19
23
|
interface AppShellProps {
|
|
20
|
-
projectName
|
|
24
|
+
projectName?: string;
|
|
21
25
|
children: ReactNode;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
|
-
function AppShellContent({ projectName, children }: AppShellProps) {
|
|
25
|
-
const pathname =
|
|
26
|
-
const
|
|
28
|
+
function AppShellContent({ projectName = 'JettyPod', children }: AppShellProps) {
|
|
29
|
+
const { pathname } = useLocation();
|
|
30
|
+
const navigate = useNavigate();
|
|
27
31
|
const showNav = !NO_NAV_PATHS.includes(pathname);
|
|
28
|
-
const
|
|
32
|
+
const isPublicPath = PUBLIC_PATHS.includes(pathname);
|
|
33
|
+
const { showDisconnected, status: connectionStatus } = useConnectionStatus();
|
|
29
34
|
|
|
30
|
-
//
|
|
35
|
+
// Optimistic auth: render content immediately, check auth in the background.
|
|
36
|
+
// Redirects to login only if the async check fails — authenticated users
|
|
37
|
+
// (the common case) never see a loader or blank screen.
|
|
31
38
|
useEffect(() => {
|
|
32
|
-
if (
|
|
33
|
-
setAuthChecked(true);
|
|
34
|
-
return;
|
|
35
|
-
}
|
|
39
|
+
if (isPublicPath || cachedAuthOk) return;
|
|
36
40
|
|
|
37
41
|
async function checkAuth() {
|
|
38
|
-
if (
|
|
42
|
+
if (isTauri()) {
|
|
39
43
|
try {
|
|
40
|
-
const status = await
|
|
44
|
+
const status = await auth.getStatus();
|
|
41
45
|
if (!status.authenticated) {
|
|
42
|
-
|
|
46
|
+
const hasLoggedIn = await auth.hasLoggedInBefore();
|
|
47
|
+
navigate(hasLoggedIn ? '/login' : '/signup', { replace: true });
|
|
43
48
|
return;
|
|
44
49
|
}
|
|
50
|
+
cachedAuthOk = true;
|
|
45
51
|
} catch {
|
|
46
|
-
|
|
47
|
-
router.push('/login');
|
|
48
|
-
return;
|
|
52
|
+
navigate('/login', { replace: true });
|
|
49
53
|
}
|
|
50
54
|
}
|
|
51
|
-
setAuthChecked(true);
|
|
52
55
|
}
|
|
53
56
|
checkAuth();
|
|
54
|
-
|
|
57
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
58
|
+
}, []);
|
|
55
59
|
|
|
56
|
-
const {
|
|
57
|
-
|
|
58
|
-
setClaudePanelOpen,
|
|
59
|
-
activeSessionId,
|
|
60
|
-
activeSession,
|
|
61
|
-
sessions,
|
|
62
|
-
standaloneSessions,
|
|
63
|
-
messages,
|
|
64
|
-
status,
|
|
65
|
-
error,
|
|
66
|
-
exitCode,
|
|
67
|
-
canRetry,
|
|
68
|
-
queuedMessage,
|
|
69
|
-
switchSession,
|
|
70
|
-
closeSession,
|
|
71
|
-
createNewSession,
|
|
72
|
-
sendMessage,
|
|
73
|
-
retry,
|
|
74
|
-
stop,
|
|
75
|
-
narratedMode,
|
|
76
|
-
toggleNarratedMode,
|
|
77
|
-
} = useClaudeSession();
|
|
60
|
+
const { claudePanelOpen } = useSessionState();
|
|
61
|
+
const { setClaudePanelOpen, openSessionPanel } = useSessionActions();
|
|
78
62
|
|
|
79
|
-
//
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
63
|
+
// Single source of truth for available content height.
|
|
64
|
+
// Sets --main-h CSS custom property on <main> so any descendant can use it
|
|
65
|
+
// without nested ResizeObservers or fragile flex-1 chains (which break in WKWebView).
|
|
66
|
+
const mainRef = useRef<HTMLElement>(null);
|
|
67
|
+
useLayoutEffect(() => {
|
|
68
|
+
const el = mainRef.current;
|
|
69
|
+
if (!el) return;
|
|
70
|
+
const ro = new ResizeObserver(([entry]) => {
|
|
71
|
+
el.style.setProperty('--main-h', `${entry.contentRect.height}px`);
|
|
72
|
+
});
|
|
73
|
+
ro.observe(el);
|
|
74
|
+
return () => ro.disconnect();
|
|
75
|
+
}, []);
|
|
83
76
|
|
|
84
77
|
return (
|
|
85
78
|
<div className="h-screen flex flex-col overflow-hidden">
|
|
86
|
-
{showNav &&
|
|
87
|
-
|
|
79
|
+
{showNav && (
|
|
80
|
+
<div className="relative flex-shrink-0">
|
|
81
|
+
<MainNav projectName={projectName} />
|
|
82
|
+
<m.button
|
|
83
|
+
onClick={() => claudePanelOpen ? setClaudePanelOpen(false) : openSessionPanel()}
|
|
84
|
+
className="absolute px-3 py-1.5 text-sm font-medium text-white hover:brightness-105 active:scale-[0.98] cursor-pointer"
|
|
85
|
+
animate={{ right: claudePanelOpen ? 480 : 0 }}
|
|
86
|
+
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
|
|
87
|
+
style={{
|
|
88
|
+
zIndex: 51,
|
|
89
|
+
top: '50%',
|
|
90
|
+
transform: 'translateY(-50%)',
|
|
91
|
+
backgroundColor: '#819D9F',
|
|
92
|
+
boxShadow: '0 1px 2px rgba(0, 0, 0, 0.06), 0 4px 12px rgba(129, 157, 159, 0.2)',
|
|
93
|
+
borderRadius: '12px 0 0 12px',
|
|
94
|
+
textAlign: 'center' as const,
|
|
95
|
+
lineHeight: 1.3,
|
|
96
|
+
}}
|
|
97
|
+
aria-label={claudePanelOpen ? 'Hide Claudes panel' : 'Show Claudes panel'}
|
|
98
|
+
data-testid="claudes-toggle-tab"
|
|
99
|
+
>
|
|
100
|
+
{claudePanelOpen ? <><span>Hide</span><br /><span>Claudes</span></> : <><span>Show</span><br /><span>Claudes</span></>}
|
|
101
|
+
</m.button>
|
|
102
|
+
</div>
|
|
103
|
+
)}
|
|
104
|
+
{showNav && showDisconnected && (
|
|
105
|
+
<div className="bg-amber-50 dark:bg-amber-950 border-b border-amber-200 dark:border-amber-800 px-4 py-2 text-center text-base text-amber-800 dark:text-amber-200" data-testid="disconnect-banner">
|
|
106
|
+
{connectionStatus === 'reconnecting'
|
|
107
|
+
? 'Reconnecting to live updates...'
|
|
108
|
+
: 'Live updates disconnected. Changes may not appear automatically.'}
|
|
109
|
+
</div>
|
|
110
|
+
)}
|
|
111
|
+
<main ref={mainRef} className={`flex-1 flex flex-col min-h-0 overflow-hidden transition-[margin] duration-200 ease-out ${showNav && claudePanelOpen ? 'mr-[480px]' : ''}`}>
|
|
88
112
|
{children}
|
|
89
113
|
</main>
|
|
90
114
|
{showNav && (
|
|
91
115
|
<ClaudePanel
|
|
92
116
|
isOpen={claudePanelOpen}
|
|
93
|
-
workItemId={activeSessionId || 'sessions'}
|
|
94
|
-
workItemTitle={activeSession?.title || 'Claude Sessions'}
|
|
95
|
-
messages={messages}
|
|
96
|
-
status={status}
|
|
97
|
-
error={error}
|
|
98
|
-
exitCode={exitCode}
|
|
99
|
-
canRetry={canRetry}
|
|
100
|
-
queuedMessage={queuedMessage}
|
|
101
117
|
onClose={() => setClaudePanelOpen(false)}
|
|
102
|
-
onRetry={retry}
|
|
103
|
-
onSendMessage={sendMessage}
|
|
104
|
-
onStop={stop}
|
|
105
|
-
sessions={sessions}
|
|
106
|
-
activeSessionId={activeSessionId}
|
|
107
|
-
onSwitchSession={switchSession}
|
|
108
|
-
standaloneSessions={standaloneSessions}
|
|
109
|
-
onNewSession={createNewSession}
|
|
110
|
-
onCloseSession={closeSession}
|
|
111
|
-
narratedMode={narratedMode}
|
|
112
|
-
onToggleNarratedMode={toggleNarratedMode}
|
|
113
118
|
/>
|
|
114
119
|
)}
|
|
115
120
|
</div>
|
|
116
121
|
);
|
|
117
122
|
}
|
|
118
123
|
|
|
119
|
-
export function AppShell({ projectName, children }: AppShellProps) {
|
|
124
|
+
export function AppShell({ projectName = 'JettyPod', children }: AppShellProps) {
|
|
120
125
|
return (
|
|
121
|
-
<
|
|
122
|
-
<
|
|
123
|
-
<
|
|
124
|
-
<
|
|
125
|
-
<
|
|
126
|
-
{
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
126
|
+
<LazyMotion features={domAnimation} strict>
|
|
127
|
+
<ConnectionStatusProvider>
|
|
128
|
+
<UsageProvider>
|
|
129
|
+
<ToastProvider>
|
|
130
|
+
<ClaudeSessionProvider>
|
|
131
|
+
<AppShellContent projectName={projectName}>
|
|
132
|
+
{children}
|
|
133
|
+
</AppShellContent>
|
|
134
|
+
</ClaudeSessionProvider>
|
|
135
|
+
</ToastProvider>
|
|
136
|
+
</UsageProvider>
|
|
137
|
+
</ConnectionStatusProvider>
|
|
138
|
+
</LazyMotion>
|
|
132
139
|
);
|
|
133
140
|
}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
'use client';
|
|
2
1
|
|
|
3
2
|
import { useState, useRef, useEffect } from 'react';
|
|
4
3
|
import { createPortal } from 'react-dom';
|
|
@@ -15,9 +14,11 @@ interface CardMenuProps {
|
|
|
15
14
|
hasActiveSession?: boolean;
|
|
16
15
|
onOpenSession?: (id: string) => void;
|
|
17
16
|
usageAllowed?: boolean;
|
|
17
|
+
onEditName?: () => void;
|
|
18
|
+
onUnaccept?: () => void;
|
|
18
19
|
}
|
|
19
20
|
|
|
20
|
-
export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescription, conversational = false, currentStatus, onStatusChange, onTriggerClaude, hasActiveSession, onOpenSession, usageAllowed = true }: CardMenuProps) {
|
|
21
|
+
export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescription, conversational = false, currentStatus, onStatusChange, onTriggerClaude, hasActiveSession, onOpenSession, usageAllowed = true, onEditName, onUnaccept }: CardMenuProps) {
|
|
21
22
|
const [isOpen, setIsOpen] = useState(false);
|
|
22
23
|
const [error, setError] = useState<string | null>(null);
|
|
23
24
|
const [dropdownPosition, setDropdownPosition] = useState<{ top: number; left: number } | null>(null);
|
|
@@ -92,21 +93,43 @@ export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescr
|
|
|
92
93
|
}
|
|
93
94
|
};
|
|
94
95
|
|
|
96
|
+
const handleEditName = (e: React.MouseEvent) => {
|
|
97
|
+
e.stopPropagation();
|
|
98
|
+
setIsOpen(false);
|
|
99
|
+
onEditName?.();
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const handleUnaccept = (e: React.MouseEvent) => {
|
|
103
|
+
e.stopPropagation();
|
|
104
|
+
setIsOpen(false);
|
|
105
|
+
onUnaccept?.();
|
|
106
|
+
};
|
|
107
|
+
|
|
95
108
|
const dropdownContent = (
|
|
96
109
|
<div
|
|
97
110
|
ref={dropdownRef}
|
|
98
|
-
className="fixed z-50 bg-white dark:bg-zinc-800 rounded-lg shadow-lg
|
|
111
|
+
className="fixed z-50 bg-white dark:bg-zinc-800 rounded-lg shadow-lg py-1.5 min-w-[140px]"
|
|
99
112
|
style={{
|
|
100
113
|
top: dropdownPosition?.top ?? 0,
|
|
101
114
|
left: dropdownPosition?.left ?? 0,
|
|
102
115
|
}}
|
|
103
116
|
data-testid="status-dropdown"
|
|
104
117
|
>
|
|
118
|
+
{onEditName && (
|
|
119
|
+
<button
|
|
120
|
+
onClick={handleEditName}
|
|
121
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
122
|
+
data-testid="edit-name-button"
|
|
123
|
+
>
|
|
124
|
+
<span className="text-zinc-500">✏️</span>
|
|
125
|
+
Edit name
|
|
126
|
+
</button>
|
|
127
|
+
)}
|
|
105
128
|
{(currentStatus === 'backlog' || currentStatus === 'cancelled') && (
|
|
106
129
|
<button
|
|
107
130
|
onClick={handleStart}
|
|
108
131
|
disabled={!usageAllowed}
|
|
109
|
-
className={`w-full px-
|
|
132
|
+
className={`w-full px-4 py-3 text-left text-base flex items-center gap-3 ${
|
|
110
133
|
usageAllowed
|
|
111
134
|
? 'text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700'
|
|
112
135
|
: 'text-zinc-400 dark:text-zinc-600 cursor-not-allowed'
|
|
@@ -114,24 +137,24 @@ export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescr
|
|
|
114
137
|
data-testid="start-button"
|
|
115
138
|
title={!usageAllowed ? 'Weekly usage limit reached' : undefined}
|
|
116
139
|
>
|
|
117
|
-
<span className={usageAllowed ? 'text-
|
|
140
|
+
<span className={usageAllowed ? 'text-[#819D9F]' : 'text-zinc-400 dark:text-zinc-600'}>▶</span>
|
|
118
141
|
Start
|
|
119
142
|
</button>
|
|
120
143
|
)}
|
|
121
144
|
{hasActiveSession && (
|
|
122
145
|
<button
|
|
123
146
|
onClick={handleOpenSession}
|
|
124
|
-
className="w-full px-
|
|
147
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
125
148
|
data-testid="open-session-button"
|
|
126
149
|
>
|
|
127
|
-
<span className="text-
|
|
150
|
+
<span className="text-[#819D9F]">💬</span>
|
|
128
151
|
Open session
|
|
129
152
|
</button>
|
|
130
153
|
)}
|
|
131
154
|
{currentStatus !== 'done' && (
|
|
132
155
|
<button
|
|
133
156
|
onClick={(e) => handleAction(e, 'done')}
|
|
134
|
-
className="w-full px-
|
|
157
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
135
158
|
data-testid="mark-done-button"
|
|
136
159
|
>
|
|
137
160
|
<span className="text-green-500">✓</span>
|
|
@@ -141,17 +164,27 @@ export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescr
|
|
|
141
164
|
{(currentStatus === 'in_progress' || currentStatus === 'done') && (
|
|
142
165
|
<button
|
|
143
166
|
onClick={(e) => handleAction(e, 'backlog')}
|
|
144
|
-
className="w-full px-
|
|
167
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
145
168
|
data-testid="unstart-button"
|
|
146
169
|
>
|
|
147
170
|
<span className="text-amber-500">↩</span>
|
|
148
171
|
Unstart
|
|
149
172
|
</button>
|
|
150
173
|
)}
|
|
174
|
+
{currentStatus === 'done' && onUnaccept && (
|
|
175
|
+
<button
|
|
176
|
+
onClick={handleUnaccept}
|
|
177
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
178
|
+
data-testid="unaccept-button"
|
|
179
|
+
>
|
|
180
|
+
<span className="text-red-500">↩</span>
|
|
181
|
+
Unaccept
|
|
182
|
+
</button>
|
|
183
|
+
)}
|
|
151
184
|
{currentStatus !== 'cancelled' && (
|
|
152
185
|
<button
|
|
153
186
|
onClick={(e) => handleAction(e, 'cancelled')}
|
|
154
|
-
className="w-full px-
|
|
187
|
+
className="w-full px-4 py-3 text-left text-base text-zinc-700 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-700 flex items-center gap-3"
|
|
155
188
|
data-testid="cancel-button"
|
|
156
189
|
>
|
|
157
190
|
<span className="text-red-500">✕</span>
|
|
@@ -166,7 +199,7 @@ export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescr
|
|
|
166
199
|
<button
|
|
167
200
|
ref={buttonRef}
|
|
168
201
|
onClick={handleMenuClick}
|
|
169
|
-
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors"
|
|
202
|
+
className="p-1 rounded hover:bg-zinc-200 dark:hover:bg-zinc-600 transition-colors duration-200 ease-out"
|
|
170
203
|
aria-label="Card menu"
|
|
171
204
|
data-testid="menu-button"
|
|
172
205
|
>
|
|
@@ -181,7 +214,7 @@ export function CardMenu({ itemId, itemTitle = '', itemType = 'chore', itemDescr
|
|
|
181
214
|
|
|
182
215
|
{error && (
|
|
183
216
|
<div
|
|
184
|
-
className="absolute right-0 top-full mt-1 z-10 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 text-xs px-
|
|
217
|
+
className="absolute right-0 top-full mt-1 z-10 bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 text-xs px-3 py-1.5 rounded border-2 border-red-200 dark:border-red-800"
|
|
185
218
|
data-testid="error-message"
|
|
186
219
|
>
|
|
187
220
|
{error}
|