kcg-code 0.0.0-stage → 0.1.0
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/README.md +228 -2
- package/bin/kcgcode.ts +13 -0
- package/bun-env.d.ts +19 -0
- package/kcg-code.config.example.json +3 -0
- package/package.json +83 -4
- package/public/logo.svg +1 -0
- package/public/manifest.json +18 -0
- package/public/sw.js +49 -0
- package/src/App.tsx +39 -0
- package/src/__tests__/manifest.test.ts +61 -0
- package/src/build-config.ts +9 -0
- package/src/cli/__tests__/dashboard.test.ts +62 -0
- package/src/cli/args.ts +123 -0
- package/src/cli/commands.ts +174 -0
- package/src/cli/dashboard.tsx +343 -0
- package/src/cli/index.ts +37 -0
- package/src/cli/theme.ts +100 -0
- package/src/components/auth/AuthGate.tsx +182 -0
- package/src/components/auth/AvatarEditor.tsx +236 -0
- package/src/components/auth/LockScreen.tsx +714 -0
- package/src/components/auth/ProfileAvatar.tsx +60 -0
- package/src/components/auth/WelcomeScreen.tsx +672 -0
- package/src/components/auth/__tests__/WelcomeScreen.test.tsx +42 -0
- package/src/components/auth/avatar-icons.tsx +114 -0
- package/src/components/layout/AppShell.tsx +142 -0
- package/src/components/layout/AppSidebar.tsx +419 -0
- package/src/components/layout/SearchDialog.tsx +189 -0
- package/src/components/layout/SidebarAccount.tsx +111 -0
- package/src/components/layout/SidebarSessionMenu.tsx +163 -0
- package/src/components/layout/ThemeToggle.tsx +67 -0
- package/src/components/projects/FolderBrowser.tsx +142 -0
- package/src/components/projects/HomeChat.tsx +250 -0
- package/src/components/projects/NewProjectDialog.tsx +249 -0
- package/src/components/projects/PanelSection.tsx +102 -0
- package/src/components/projects/ProjectInstructions.tsx +229 -0
- package/src/components/projects/ProjectRow.tsx +58 -0
- package/src/components/projects/ProjectSidePanel.tsx +448 -0
- package/src/components/sessions/AgentModeList.tsx +265 -0
- package/src/components/sessions/AgentPicker.tsx +123 -0
- package/src/components/sessions/AssistantTurn.tsx +145 -0
- package/src/components/sessions/AttachmentTile.tsx +161 -0
- package/src/components/sessions/CodeBlock.tsx +107 -0
- package/src/components/sessions/ComposerActionSheet.tsx +96 -0
- package/src/components/sessions/ConfirmDeleteProjectDialog.tsx +80 -0
- package/src/components/sessions/ConfirmSessionDeleteDialog.tsx +72 -0
- package/src/components/sessions/ContextPanel.tsx +561 -0
- package/src/components/sessions/DropPlate.tsx +165 -0
- package/src/components/sessions/FileDropOverlay.tsx +232 -0
- package/src/components/sessions/FileMentionDropdown.tsx +47 -0
- package/src/components/sessions/ImageLightbox.tsx +173 -0
- package/src/components/sessions/MarkdownContent.tsx +187 -0
- package/src/components/sessions/Mascot.tsx +47 -0
- package/src/components/sessions/MessageActions.tsx +60 -0
- package/src/components/sessions/MessageRail.tsx +121 -0
- package/src/components/sessions/ModelPicker.tsx +209 -0
- package/src/components/sessions/ModelSearchList.tsx +151 -0
- package/src/components/sessions/NewSessionDialog.tsx +189 -0
- package/src/components/sessions/PendingAttachments.tsx +127 -0
- package/src/components/sessions/PromptCard.tsx +1085 -0
- package/src/components/sessions/PromptPanel.tsx +112 -0
- package/src/components/sessions/SessionComposer.tsx +640 -0
- package/src/components/sessions/SessionHeader.tsx +175 -0
- package/src/components/sessions/SessionList.tsx +396 -0
- package/src/components/sessions/SessionRow.tsx +137 -0
- package/src/components/sessions/SessionTimeline.tsx +275 -0
- package/src/components/sessions/SessionTimelineSkeleton.tsx +83 -0
- package/src/components/sessions/SessionView.tsx +236 -0
- package/src/components/sessions/ThoughtProcessBlock.tsx +199 -0
- package/src/components/sessions/UserMessage.tsx +177 -0
- package/src/components/sessions/__tests__/ContextPanel.test.tsx +220 -0
- package/src/components/sessions/__tests__/MarkdownContent.test.tsx +89 -0
- package/src/components/sessions/__tests__/MessageRail.test.tsx +40 -0
- package/src/components/sessions/__tests__/PermissionCard.test.tsx +79 -0
- package/src/components/sessions/__tests__/PromptCard.test.ts +166 -0
- package/src/components/sessions/__tests__/QuestionCard.test.tsx +166 -0
- package/src/components/sessions/__tests__/SessionTimelineSkeleton.test.tsx +32 -0
- package/src/components/sessions/__tests__/SessionView.test.ts +386 -0
- package/src/components/settings/AboutGroup.tsx +170 -0
- package/src/components/settings/AccountGroup.tsx +122 -0
- package/src/components/settings/DevicesGroup.tsx +151 -0
- package/src/components/settings/LhrRemoteAccess.tsx +262 -0
- package/src/components/settings/PasskeysGroup.tsx +551 -0
- package/src/components/settings/RemoteAccessGroup.tsx +1226 -0
- package/src/components/settings/SecurityGroup.tsx +566 -0
- package/src/components/settings/WorkspaceGroup.tsx +370 -0
- package/src/components/settings/__tests__/AboutGroup.test.tsx +47 -0
- package/src/components/settings/__tests__/PasskeysGroup.test.tsx +109 -0
- package/src/components/settings/__tests__/WorkspaceGroup.test.tsx +49 -0
- package/src/components/settings/prefs.tsx +265 -0
- package/src/components/skills/InstallNotifications.tsx +325 -0
- package/src/components/skills/InstallSkillDialog.tsx +233 -0
- package/src/components/skills/SkillCards.tsx +210 -0
- package/src/components/skills/SkillsProjectPicker.tsx +178 -0
- package/src/components/ui/alert-dialog.tsx +179 -0
- package/src/components/ui/alert.tsx +60 -0
- package/src/components/ui/badge.tsx +46 -0
- package/src/components/ui/bubble.tsx +125 -0
- package/src/components/ui/button.tsx +62 -0
- package/src/components/ui/card.tsx +75 -0
- package/src/components/ui/collapsible.tsx +19 -0
- package/src/components/ui/dialog.tsx +142 -0
- package/src/components/ui/dropdown-menu.tsx +225 -0
- package/src/components/ui/empty.tsx +94 -0
- package/src/components/ui/field.tsx +234 -0
- package/src/components/ui/input-group.tsx +161 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/label.tsx +19 -0
- package/src/components/ui/message-scroller.tsx +131 -0
- package/src/components/ui/message.tsx +85 -0
- package/src/components/ui/popover.tsx +46 -0
- package/src/components/ui/select.tsx +172 -0
- package/src/components/ui/separator.tsx +28 -0
- package/src/components/ui/sheet.tsx +138 -0
- package/src/components/ui/sidebar.tsx +699 -0
- package/src/components/ui/skeleton.tsx +15 -0
- package/src/components/ui/sonner.tsx +45 -0
- package/src/components/ui/spinner.tsx +16 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/tooltip.tsx +51 -0
- package/src/config.ts +85 -0
- package/src/db.ts +146 -0
- package/src/frontend.tsx +51 -0
- package/src/hooks/__tests__/useMention.test.ts +79 -0
- package/src/hooks/__tests__/useResyncOnReconnect.test.ts +128 -0
- package/src/hooks/__tests__/useRouter.test.ts +17 -0
- package/src/hooks/__tests__/useSmoothText.react.test.ts +194 -0
- package/src/hooks/__tests__/useSmoothText.test.ts +61 -0
- package/src/hooks/__tests__/useTheme.test.ts +118 -0
- package/src/hooks/use-mobile.ts +16 -0
- package/src/hooks/useAgentPicker.ts +97 -0
- package/src/hooks/useMediaQuery.ts +49 -0
- package/src/hooks/useMention.ts +236 -0
- package/src/hooks/useProjects.ts +88 -0
- package/src/hooks/useResyncOnReconnect.ts +28 -0
- package/src/hooks/useRouter.ts +92 -0
- package/src/hooks/useSessionChat.ts +427 -0
- package/src/hooks/useSessionContext.ts +106 -0
- package/src/hooks/useSessionList.ts +234 -0
- package/src/hooks/useSidebarData.ts +108 -0
- package/src/hooks/useSmoothText.ts +122 -0
- package/src/hooks/useTheme.ts +164 -0
- package/src/hooks/useWebSocket.ts +196 -0
- package/src/index.css +27 -0
- package/src/index.html +14 -0
- package/src/index.ts +49 -0
- package/src/lib/__tests__/attachments.test.ts +58 -0
- package/src/lib/__tests__/avatar-presets.test.ts +25 -0
- package/src/lib/__tests__/collapsible.test.ts +101 -0
- package/src/lib/__tests__/composer.test.ts +118 -0
- package/src/lib/__tests__/highlight.test.ts +42 -0
- package/src/lib/__tests__/home-chat.test.ts +64 -0
- package/src/lib/__tests__/lock-screen.test.ts +64 -0
- package/src/lib/__tests__/model-picker.test.ts +205 -0
- package/src/lib/__tests__/onboarding.test.ts +41 -0
- package/src/lib/__tests__/permission-info.test.ts +61 -0
- package/src/lib/__tests__/project-extensions.test.ts +52 -0
- package/src/lib/__tests__/project-overview.test.ts +173 -0
- package/src/lib/__tests__/references.test.ts +121 -0
- package/src/lib/__tests__/routes.test.ts +134 -0
- package/src/lib/__tests__/session-summary.test.ts +115 -0
- package/src/lib/__tests__/session-title.test.ts +58 -0
- package/src/lib/__tests__/sidebar-groups.test.ts +100 -0
- package/src/lib/__tests__/skill-installs.test.ts +134 -0
- package/src/lib/__tests__/skills-catalog.test.ts +96 -0
- package/src/lib/__tests__/tone.test.ts +37 -0
- package/src/lib/__tests__/turns-delta.test.ts +55 -0
- package/src/lib/api.ts +306 -0
- package/src/lib/attachments.ts +203 -0
- package/src/lib/auth.ts +92 -0
- package/src/lib/avatar-presets.ts +31 -0
- package/src/lib/collapsible.ts +75 -0
- package/src/lib/composer.ts +89 -0
- package/src/lib/data-events.ts +17 -0
- package/src/lib/headlines.ts +30 -0
- package/src/lib/highlight.ts +186 -0
- package/src/lib/layout.ts +20 -0
- package/src/lib/lock-screen.ts +54 -0
- package/src/lib/model-picker.ts +133 -0
- package/src/lib/onboarding.ts +38 -0
- package/src/lib/passkeys.ts +113 -0
- package/src/lib/pending-prompt.ts +53 -0
- package/src/lib/permission-info.ts +101 -0
- package/src/lib/project-extensions.ts +74 -0
- package/src/lib/project-overview.ts +117 -0
- package/src/lib/prompts.ts +87 -0
- package/src/lib/references.ts +106 -0
- package/src/lib/routes.ts +115 -0
- package/src/lib/session-handoff.ts +25 -0
- package/src/lib/session-status.ts +45 -0
- package/src/lib/session-summary.ts +68 -0
- package/src/lib/session-title.ts +63 -0
- package/src/lib/sidebar-groups.ts +78 -0
- package/src/lib/skill-installs.ts +360 -0
- package/src/lib/skills-catalog.ts +99 -0
- package/src/lib/time.ts +22 -0
- package/src/lib/tone.ts +80 -0
- package/src/lib/turns.ts +320 -0
- package/src/lib/utils.ts +6 -0
- package/src/logo.svg +31 -0
- package/src/pages/projects/ProjectDetailPage.tsx +130 -0
- package/src/pages/projects/ProjectsPage.tsx +97 -0
- package/src/pages/sessions/SessionPage.tsx +122 -0
- package/src/pages/settings/SettingsPage.tsx +299 -0
- package/src/pages/settings/__tests__/SettingsPage.test.ts +9 -0
- package/src/pages/skills/SkillsPage.tsx +701 -0
- package/src/paths.ts +121 -0
- package/src/runtime.ts +24 -0
- package/src/server/__tests__/app.test.ts +1031 -0
- package/src/server/__tests__/auth.test.ts +370 -0
- package/src/server/__tests__/config.test.ts +101 -0
- package/src/server/__tests__/db.test.ts +485 -0
- package/src/server/__tests__/host.test.ts +14 -0
- package/src/server/__tests__/tunnel-config.test.ts +38 -0
- package/src/server/app.ts +415 -0
- package/src/server/db/auth.ts +311 -0
- package/src/server/db/messages.ts +50 -0
- package/src/server/db/projects.ts +106 -0
- package/src/server/db/prompts.ts +78 -0
- package/src/server/db/rows.ts +150 -0
- package/src/server/db/schema.ts +221 -0
- package/src/server/db/sessions.ts +239 -0
- package/src/server/db/tunnel.ts +124 -0
- package/src/server/host.ts +12 -0
- package/src/server/result.ts +15 -0
- package/src/server/routes/auth-guard.ts +156 -0
- package/src/server/routes/auth.routes.ts +287 -0
- package/src/server/routes/helpers.ts +165 -0
- package/src/server/routes/meta.routes.ts +24 -0
- package/src/server/routes/passkeys.routes.ts +161 -0
- package/src/server/routes/projects.routes.ts +157 -0
- package/src/server/routes/sessions.routes.ts +197 -0
- package/src/server/routes/skills.routes.ts +117 -0
- package/src/server/routes/tunnel.routes.ts +132 -0
- package/src/server/routes/types.ts +35 -0
- package/src/server/routes/uploads.routes.ts +54 -0
- package/src/server/routes/workspace.routes.ts +127 -0
- package/src/server/services/__tests__/app-info.test.ts +18 -0
- package/src/server/services/__tests__/attachments.test.ts +159 -0
- package/src/server/services/__tests__/auth-passkeys.test.ts +358 -0
- package/src/server/services/__tests__/auth.test.ts +341 -0
- package/src/server/services/__tests__/frpc-installer.test.ts +83 -0
- package/src/server/services/__tests__/git-info.test.ts +74 -0
- package/src/server/services/__tests__/opencode.test.ts +320 -0
- package/src/server/services/__tests__/project-manager.test.ts +190 -0
- package/src/server/services/__tests__/sandbox.test.ts +141 -0
- package/src/server/services/__tests__/session-manager.test.ts +3469 -0
- package/src/server/services/__tests__/skill-install-jobs.test.ts +226 -0
- package/src/server/services/__tests__/skills-registry.test.ts +359 -0
- package/src/server/services/__tests__/tunnel-lhr.test.ts +234 -0
- package/src/server/services/__tests__/tunnel.test.ts +367 -0
- package/src/server/services/__tests__/websocket-gateway.test.ts +677 -0
- package/src/server/services/__tests__/workspace.test.ts +249 -0
- package/src/server/services/app-info.ts +29 -0
- package/src/server/services/attachments.ts +193 -0
- package/src/server/services/auth-passkeys.ts +459 -0
- package/src/server/services/auth.ts +633 -0
- package/src/server/services/frpc-installer.ts +183 -0
- package/src/server/services/git-info.ts +87 -0
- package/src/server/services/opencode-client.ts +714 -0
- package/src/server/services/opencode-server.ts +319 -0
- package/src/server/services/opencode-sse.ts +82 -0
- package/src/server/services/project-manager.ts +147 -0
- package/src/server/services/sandbox.ts +83 -0
- package/src/server/services/session-events.ts +256 -0
- package/src/server/services/session-manager.ts +1374 -0
- package/src/server/services/skill-install-jobs.ts +262 -0
- package/src/server/services/skills-registry.ts +468 -0
- package/src/server/services/tunnel-api.ts +207 -0
- package/src/server/services/tunnel-config.ts +45 -0
- package/src/server/services/tunnel-lhr.ts +387 -0
- package/src/server/services/tunnel.ts +633 -0
- package/src/server/services/turn-stream.ts +339 -0
- package/src/server/services/websocket-gateway.ts +374 -0
- package/src/server/services/workspace.ts +223 -0
- package/src/server/services/ws-transport.ts +121 -0
- package/src/types/context.ts +69 -0
- package/src/types/index.ts +13 -0
- package/src/types/message.ts +58 -0
- package/src/types/project.ts +16 -0
- package/src/types/prompt.ts +118 -0
- package/src/types/session.ts +52 -0
- package/src/types/turns.ts +47 -0
- package/src/ws-protocol.ts +93 -0
- package/styles/globals.css +311 -0
- package/tsconfig.json +37 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Welcome screen — render statis langkah pertama.
|
|
3
|
+
*/
|
|
4
|
+
import { expect, test } from "bun:test";
|
|
5
|
+
import { domAnimation, LazyMotion } from "motion/react";
|
|
6
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
7
|
+
import type { AuthStatus } from "@/server/services/auth";
|
|
8
|
+
import { WelcomeScreen } from "../WelcomeScreen";
|
|
9
|
+
|
|
10
|
+
const status: AuthStatus = {
|
|
11
|
+
protected: false,
|
|
12
|
+
authenticated: true,
|
|
13
|
+
lockKind: null,
|
|
14
|
+
autoLockMinutes: 15,
|
|
15
|
+
profile: { nickname: null, avatarUrl: null, avatarPreset: null },
|
|
16
|
+
retryAfterSec: 0,
|
|
17
|
+
needsOnboarding: true,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
const html = () =>
|
|
21
|
+
renderToStaticMarkup(
|
|
22
|
+
<LazyMotion features={domAnimation} strict>
|
|
23
|
+
<WelcomeScreen status={status} onFinished={() => {}} />
|
|
24
|
+
</LazyMotion>,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
test("langkah pertama: dialog berlabel, logo, judul, Get started & Skip", () => {
|
|
28
|
+
const out = html();
|
|
29
|
+
expect(out).toContain('role="dialog"');
|
|
30
|
+
expect(out).toContain('aria-labelledby="welcome-title"');
|
|
31
|
+
expect(out).toContain("Welcome to KCG Code");
|
|
32
|
+
expect(out).toContain("Get started");
|
|
33
|
+
expect(out).toContain("Skip for now");
|
|
34
|
+
// Ringkasan langkah yang akan datang.
|
|
35
|
+
expect(out).toContain("Protect it with a PIN or password");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("langkah pertama tanpa tombol Back & titik progres", () => {
|
|
39
|
+
const out = html();
|
|
40
|
+
expect(out).not.toContain('aria-label="Back"');
|
|
41
|
+
expect(out).not.toContain('aria-label="Setup progress"');
|
|
42
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pemetaan avatar bawaan (id preset) -> ikon lucide + warna latar.
|
|
3
|
+
* Id-nya didefinisikan di `lib/avatar-presets.ts` (dipakai server juga).
|
|
4
|
+
*/
|
|
5
|
+
import {
|
|
6
|
+
BirdIcon,
|
|
7
|
+
BotIcon,
|
|
8
|
+
CatIcon,
|
|
9
|
+
CoffeeIcon,
|
|
10
|
+
DogIcon,
|
|
11
|
+
FishIcon,
|
|
12
|
+
Flower2Icon,
|
|
13
|
+
Gamepad2Icon,
|
|
14
|
+
GhostIcon,
|
|
15
|
+
LeafIcon,
|
|
16
|
+
type LucideIcon,
|
|
17
|
+
MountainIcon,
|
|
18
|
+
PandaIcon,
|
|
19
|
+
RabbitIcon,
|
|
20
|
+
RocketIcon,
|
|
21
|
+
SquirrelIcon,
|
|
22
|
+
TurtleIcon,
|
|
23
|
+
} from "lucide-react";
|
|
24
|
+
import type { AvatarPresetId } from "@/lib/avatar-presets";
|
|
25
|
+
|
|
26
|
+
export interface AvatarPreset {
|
|
27
|
+
label: string;
|
|
28
|
+
Icon: LucideIcon;
|
|
29
|
+
/** Kelas latar + warna ikon (terang & gelap). */
|
|
30
|
+
className: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const AVATAR_PRESETS: Record<AvatarPresetId, AvatarPreset> = {
|
|
34
|
+
cat: {
|
|
35
|
+
label: "Cat",
|
|
36
|
+
Icon: CatIcon,
|
|
37
|
+
className: "bg-orange-500/15 text-orange-600 dark:text-orange-400",
|
|
38
|
+
},
|
|
39
|
+
dog: {
|
|
40
|
+
label: "Dog",
|
|
41
|
+
Icon: DogIcon,
|
|
42
|
+
className: "bg-amber-500/15 text-amber-700 dark:text-amber-400",
|
|
43
|
+
},
|
|
44
|
+
rabbit: {
|
|
45
|
+
label: "Rabbit",
|
|
46
|
+
Icon: RabbitIcon,
|
|
47
|
+
className: "bg-pink-500/15 text-pink-600 dark:text-pink-400",
|
|
48
|
+
},
|
|
49
|
+
panda: {
|
|
50
|
+
label: "Panda",
|
|
51
|
+
Icon: PandaIcon,
|
|
52
|
+
className: "bg-zinc-500/15 text-zinc-700 dark:text-zinc-300",
|
|
53
|
+
},
|
|
54
|
+
bird: {
|
|
55
|
+
label: "Bird",
|
|
56
|
+
Icon: BirdIcon,
|
|
57
|
+
className: "bg-sky-500/15 text-sky-600 dark:text-sky-400",
|
|
58
|
+
},
|
|
59
|
+
fish: {
|
|
60
|
+
label: "Fish",
|
|
61
|
+
Icon: FishIcon,
|
|
62
|
+
className: "bg-cyan-500/15 text-cyan-700 dark:text-cyan-400",
|
|
63
|
+
},
|
|
64
|
+
turtle: {
|
|
65
|
+
label: "Turtle",
|
|
66
|
+
Icon: TurtleIcon,
|
|
67
|
+
className: "bg-emerald-500/15 text-emerald-700 dark:text-emerald-400",
|
|
68
|
+
},
|
|
69
|
+
squirrel: {
|
|
70
|
+
label: "Squirrel",
|
|
71
|
+
Icon: SquirrelIcon,
|
|
72
|
+
className: "bg-yellow-500/15 text-yellow-700 dark:text-yellow-400",
|
|
73
|
+
},
|
|
74
|
+
rocket: {
|
|
75
|
+
label: "Rocket",
|
|
76
|
+
Icon: RocketIcon,
|
|
77
|
+
className: "bg-violet-500/15 text-violet-600 dark:text-violet-400",
|
|
78
|
+
},
|
|
79
|
+
bot: {
|
|
80
|
+
label: "Robot",
|
|
81
|
+
Icon: BotIcon,
|
|
82
|
+
className: "bg-blue-500/15 text-blue-600 dark:text-blue-400",
|
|
83
|
+
},
|
|
84
|
+
ghost: {
|
|
85
|
+
label: "Ghost",
|
|
86
|
+
Icon: GhostIcon,
|
|
87
|
+
className: "bg-indigo-500/15 text-indigo-600 dark:text-indigo-400",
|
|
88
|
+
},
|
|
89
|
+
gamepad: {
|
|
90
|
+
label: "Gamepad",
|
|
91
|
+
Icon: Gamepad2Icon,
|
|
92
|
+
className: "bg-fuchsia-500/15 text-fuchsia-600 dark:text-fuchsia-400",
|
|
93
|
+
},
|
|
94
|
+
flower: {
|
|
95
|
+
label: "Flower",
|
|
96
|
+
Icon: Flower2Icon,
|
|
97
|
+
className: "bg-rose-500/15 text-rose-600 dark:text-rose-400",
|
|
98
|
+
},
|
|
99
|
+
leaf: {
|
|
100
|
+
label: "Leaf",
|
|
101
|
+
Icon: LeafIcon,
|
|
102
|
+
className: "bg-lime-500/15 text-lime-700 dark:text-lime-400",
|
|
103
|
+
},
|
|
104
|
+
mountain: {
|
|
105
|
+
label: "Mountain",
|
|
106
|
+
Icon: MountainIcon,
|
|
107
|
+
className: "bg-teal-500/15 text-teal-700 dark:text-teal-400",
|
|
108
|
+
},
|
|
109
|
+
coffee: {
|
|
110
|
+
label: "Coffee",
|
|
111
|
+
Icon: CoffeeIcon,
|
|
112
|
+
className: "bg-stone-500/15 text-stone-700 dark:text-stone-300",
|
|
113
|
+
},
|
|
114
|
+
};
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell aplikasi KCG Code (task 25.1) — layout + routing URL ala kcgcode.
|
|
3
|
+
*
|
|
4
|
+
* Navigasi: `/` (daftar Project) -> `/projects/:projectId` (daftar Session)
|
|
5
|
+
* -> `/projects/:projectId/sessions/:sessionId` (Session view).
|
|
6
|
+
* - Rute halaman didaftarkan eksplisit di `src/index.ts` (`spaPaths`), lalu
|
|
7
|
+
* di sini `useRouter()` (pathname) + `parseRoute()` memilih halaman dari
|
|
8
|
+
* `src/pages/*` — tiap halaman me-resolve ulang datanya dari API sehingga
|
|
9
|
+
* refresh/direct link tetap berfungsi.
|
|
10
|
+
* - Toggle tema (`ThemeToggle`, Req 8.2/8.6) ada di footer `AppSidebar`.
|
|
11
|
+
* - Halaman Session di-`key` per id agar remount saat pindah Session.
|
|
12
|
+
* - `AppSidebar` (shadcn `sidebar`) menampilkan seluruh Session digroup per
|
|
13
|
+
* Project: panel tetap di desktop, Sheet off-canvas di HP (`SidebarTrigger`
|
|
14
|
+
* di header shell & header Session).
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { AppSidebar } from "@/components/layout/AppSidebar";
|
|
18
|
+
import { InstallNotifications } from "@/components/skills/InstallNotifications";
|
|
19
|
+
import { SidebarInset, SidebarProvider, SidebarTrigger } from "@/components/ui/sidebar";
|
|
20
|
+
import { TooltipProvider } from "@/components/ui/tooltip";
|
|
21
|
+
import { useRouter } from "@/hooks/useRouter";
|
|
22
|
+
import { useSidebarData } from "@/hooks/useSidebarData";
|
|
23
|
+
import { parseRoute, projectsPath } from "@/lib/routes";
|
|
24
|
+
import { cn } from "@/lib/utils";
|
|
25
|
+
import { ProjectDetailPage } from "@/pages/projects/ProjectDetailPage";
|
|
26
|
+
import { ProjectsPage } from "@/pages/projects/ProjectsPage";
|
|
27
|
+
import { SessionPage } from "@/pages/sessions/SessionPage";
|
|
28
|
+
import { SettingsPage } from "@/pages/settings/SettingsPage";
|
|
29
|
+
import { SkillsPage } from "@/pages/skills/SkillsPage";
|
|
30
|
+
|
|
31
|
+
import logo from "../../logo.svg";
|
|
32
|
+
|
|
33
|
+
export function AppShell() {
|
|
34
|
+
const { pathname } = useRouter();
|
|
35
|
+
const route = parseRoute(pathname);
|
|
36
|
+
const sidebarData = useSidebarData(pathname);
|
|
37
|
+
|
|
38
|
+
const isHome = route.name === "projects";
|
|
39
|
+
/**
|
|
40
|
+
* Session view punya header sendiri (back, nama model, play/stop, menu aksi
|
|
41
|
+
* berisi tema & hapus), jadi chrome shell disembunyikan di sana — dua header
|
|
42
|
+
* bertumpuk memakan tinggi layar HP tanpa menambah informasi.
|
|
43
|
+
*/
|
|
44
|
+
const isSession = route.name === "session";
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<TooltipProvider>
|
|
48
|
+
<SidebarProvider className="h-dvh min-h-0 overflow-hidden">
|
|
49
|
+
<AppSidebar route={route} data={sidebarData} />
|
|
50
|
+
<SidebarInset className="min-h-0 min-w-0 overflow-hidden">
|
|
51
|
+
{/* Area konten memenuhi SidebarInset — sidebar sudah jadi pembatas
|
|
52
|
+
kiri, jadi tak perlu lagi kolom ber-border di tengah layar. Lebar
|
|
53
|
+
baca dibatasi per halaman (lihat `<main>` di bawah). */}
|
|
54
|
+
<div className="relative flex h-full w-full flex-col overflow-x-hidden bg-background">
|
|
55
|
+
{/* Header (kecuali Session view yang membawa header sendiri) */}
|
|
56
|
+
{!isSession && (
|
|
57
|
+
<header className="flex w-full shrink-0 items-center gap-3 px-3 py-3 sm:px-4">
|
|
58
|
+
<SidebarTrigger className="size-9 shrink-0" />
|
|
59
|
+
{/* Brand hanya di HP — di desktop sudah ada di header sidebar. */}
|
|
60
|
+
<span className="flex min-w-0 items-center gap-2 md:hidden">
|
|
61
|
+
<span className="flex size-7 shrink-0 items-center justify-center rounded-lg bg-primary p-1">
|
|
62
|
+
<img src={logo} alt="" className="size-full" />
|
|
63
|
+
</span>
|
|
64
|
+
<span className="truncate text-sm font-semibold tracking-tight">KCG Code</span>
|
|
65
|
+
</span>
|
|
66
|
+
</header>
|
|
67
|
+
)}
|
|
68
|
+
|
|
69
|
+
{/* Konten per URL */}
|
|
70
|
+
{route.name === "session" ? (
|
|
71
|
+
<SessionPage
|
|
72
|
+
key={`${route.projectId}/${route.sessionId}`}
|
|
73
|
+
projectId={route.projectId}
|
|
74
|
+
sessionId={route.sessionId}
|
|
75
|
+
/>
|
|
76
|
+
) : (
|
|
77
|
+
<main
|
|
78
|
+
className={cn(
|
|
79
|
+
"min-h-0 flex-1 overflow-x-hidden overflow-y-auto bg-background",
|
|
80
|
+
// Detail Project lg+: kolom-kolomnya yang scroll, bukan <main>.
|
|
81
|
+
route.name === "project" && "lg:overflow-y-hidden",
|
|
82
|
+
)}
|
|
83
|
+
>
|
|
84
|
+
<div
|
|
85
|
+
className={
|
|
86
|
+
isHome
|
|
87
|
+
? /* Homepage rata atas (bukan center): daftar Project adalah
|
|
88
|
+
fokus, jadi baris pertama langsung terlihat tanpa gap
|
|
89
|
+
atas besar — dan daftar yang lebih tinggi dari layar
|
|
90
|
+
tetap bisa di-scroll seluruhnya. */
|
|
91
|
+
"flex min-h-full flex-col px-4 py-6 sm:px-6 sm:py-10"
|
|
92
|
+
: route.name === "project"
|
|
93
|
+
? /* Detail Project: dua kolom (Session + panel samping).
|
|
94
|
+
Layar lg+: tinggi dikunci ke viewport (`h-full`),
|
|
95
|
+
tiap kolom scroll sendiri; lebih sempit: halaman
|
|
96
|
+
scroll biasa (panel samping ada di Sheet). */
|
|
97
|
+
"mx-auto w-full max-w-6xl px-4 pt-2 pb-12 sm:px-6 sm:pt-4 sm:pb-16 lg:h-full lg:pb-0"
|
|
98
|
+
: route.name === "skills"
|
|
99
|
+
? /* Skills: grid kartu 2 kolom butuh ruang lebih lebar. */
|
|
100
|
+
"mx-auto w-full max-w-5xl px-4 pt-2 pb-12 sm:px-6 sm:pt-4 sm:pb-16"
|
|
101
|
+
: "mx-auto w-full max-w-3xl px-4 pt-2 pb-12 sm:px-6 sm:pt-4 sm:pb-16"
|
|
102
|
+
}
|
|
103
|
+
>
|
|
104
|
+
{route.name === "projects" && <ProjectsPage />}
|
|
105
|
+
{route.name === "project" && (
|
|
106
|
+
<ProjectDetailPage key={route.projectId} projectId={route.projectId} />
|
|
107
|
+
)}
|
|
108
|
+
{route.name === "skills" && <SkillsPage />}
|
|
109
|
+
{route.name === "settings" && (
|
|
110
|
+
<SettingsPage section={route.section} anchor={route.anchor} />
|
|
111
|
+
)}
|
|
112
|
+
{route.name === "not-found" && <NotFoundView />}
|
|
113
|
+
</div>
|
|
114
|
+
</main>
|
|
115
|
+
)}
|
|
116
|
+
</div>
|
|
117
|
+
</SidebarInset>
|
|
118
|
+
</SidebarProvider>
|
|
119
|
+
{/* Notifikasi instalasi skill: global agar tetap tampil di halaman mana pun. */}
|
|
120
|
+
<InstallNotifications />
|
|
121
|
+
</TooltipProvider>
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function NotFoundView() {
|
|
126
|
+
const { navigate } = useRouter();
|
|
127
|
+
return (
|
|
128
|
+
<div className="flex flex-col items-center gap-4 py-10 text-center">
|
|
129
|
+
<p className="text-sm text-muted-foreground">Page not found.</p>
|
|
130
|
+
<a
|
|
131
|
+
href={projectsPath()}
|
|
132
|
+
onClick={(e) => {
|
|
133
|
+
e.preventDefault();
|
|
134
|
+
navigate(projectsPath());
|
|
135
|
+
}}
|
|
136
|
+
className="text-sm font-medium underline underline-offset-4"
|
|
137
|
+
>
|
|
138
|
+
Back to projects
|
|
139
|
+
</a>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
}
|