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,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Panel Remote access via localhost.run — tanpa akun, lewat `ssh` bawaan OS.
|
|
3
|
+
*
|
|
4
|
+
* Satu grup ala AdwPreferencesGroup:
|
|
5
|
+
* - AdwSwitchRow "Remote access" (status satu frasa di subjudul),
|
|
6
|
+
* - QR + link saat online (komponen bersama penyedia bawaan),
|
|
7
|
+
* - baris expander "Connection log".
|
|
8
|
+
* Catatan alamat yang berubah + tautan ke KCG Code link jadi footer grup.
|
|
9
|
+
*/
|
|
10
|
+
import { ChevronDownIcon, GlobeIcon, LockIcon, TerminalSquareIcon } from "lucide-react";
|
|
11
|
+
import { type ReactNode, useCallback, useEffect, useState } from "react";
|
|
12
|
+
import { toast } from "sonner";
|
|
13
|
+
import { ActionRow, PrefsGroup } from "@/components/settings/prefs";
|
|
14
|
+
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
|
15
|
+
import { Spinner } from "@/components/ui/spinner";
|
|
16
|
+
import { Switch } from "@/components/ui/switch";
|
|
17
|
+
import { useRouter } from "@/hooks/useRouter";
|
|
18
|
+
import { ApiError, apiErrorMessage, apiFetch } from "@/lib/api";
|
|
19
|
+
import { settingsPath } from "@/lib/routes";
|
|
20
|
+
import { cn } from "@/lib/utils";
|
|
21
|
+
import type { LhrStatus } from "@/server/services/tunnel-lhr";
|
|
22
|
+
|
|
23
|
+
const BUSY: LhrStatus["phase"][] = ["starting", "connecting", "reconnecting"];
|
|
24
|
+
|
|
25
|
+
function errorText(e: unknown, fallback: string): string {
|
|
26
|
+
return e instanceof ApiError ? e.message : fallback;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface LhrRemoteAccessProps {
|
|
30
|
+
protectedApp: boolean;
|
|
31
|
+
/** Komponen bersama dari panel penyedia bawaan (QR + link, log). */
|
|
32
|
+
LiveAddress: (p: { url: string }) => ReactNode;
|
|
33
|
+
LogPanel: (p: { endpoint?: string }) => ReactNode;
|
|
34
|
+
/** Buka konfirmasi mematikan (halaman bisa terputus bila dibuka lewat link ini). */
|
|
35
|
+
onRequestStop: (stop: () => Promise<void>, viaThisLink: boolean) => void;
|
|
36
|
+
/** Pindah ke penyedia yang direkomendasikan (KCG Code link). */
|
|
37
|
+
onUseRecommended: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function LhrRemoteAccess({
|
|
41
|
+
protectedApp,
|
|
42
|
+
LiveAddress,
|
|
43
|
+
LogPanel,
|
|
44
|
+
onRequestStop,
|
|
45
|
+
onUseRecommended,
|
|
46
|
+
}: LhrRemoteAccessProps) {
|
|
47
|
+
const { navigate } = useRouter();
|
|
48
|
+
const [s, setS] = useState<LhrStatus | null>(null);
|
|
49
|
+
const [busy, setBusy] = useState<"start" | "stop" | null>(null);
|
|
50
|
+
const [showLog, setShowLog] = useState(false);
|
|
51
|
+
|
|
52
|
+
const load = useCallback(async () => {
|
|
53
|
+
try {
|
|
54
|
+
const res = await apiFetch("/api/tunnel/lhr");
|
|
55
|
+
setS(((await res.json()) as { lhr: LhrStatus }).lhr);
|
|
56
|
+
} catch {
|
|
57
|
+
/* poll berikutnya mencoba lagi */
|
|
58
|
+
}
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
const active = s !== null && BUSY.includes(s.phase);
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
void load();
|
|
64
|
+
const id = setInterval(() => void load(), active ? 1500 : 5000);
|
|
65
|
+
return () => clearInterval(id);
|
|
66
|
+
}, [load, active]);
|
|
67
|
+
|
|
68
|
+
const start = async () => {
|
|
69
|
+
setBusy("start");
|
|
70
|
+
try {
|
|
71
|
+
const res = await apiFetch("/api/tunnel/lhr/start", { method: "POST", body: "{}" });
|
|
72
|
+
setS(((await res.json()) as { lhr: LhrStatus }).lhr);
|
|
73
|
+
} catch (e) {
|
|
74
|
+
toast.error(errorText(e, "Couldn't turn on remote access."));
|
|
75
|
+
void load();
|
|
76
|
+
} finally {
|
|
77
|
+
setBusy(null);
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const stop = async () => {
|
|
82
|
+
setBusy("stop");
|
|
83
|
+
try {
|
|
84
|
+
const res = await apiFetch("/api/tunnel/lhr/stop", { method: "POST", body: "{}" });
|
|
85
|
+
setS(((await res.json()) as { lhr: LhrStatus }).lhr);
|
|
86
|
+
toast.success("Remote access turned off.");
|
|
87
|
+
} catch (e) {
|
|
88
|
+
toast.error(errorText(e, "Couldn't turn off remote access."));
|
|
89
|
+
} finally {
|
|
90
|
+
setBusy(null);
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
if (!s) {
|
|
95
|
+
return (
|
|
96
|
+
<PrefsGroup>
|
|
97
|
+
<li className="flex min-h-14 items-center justify-center" role="status">
|
|
98
|
+
<Spinner className="size-5 text-muted-foreground" />
|
|
99
|
+
<span className="sr-only">Loading remote access…</span>
|
|
100
|
+
</li>
|
|
101
|
+
</PrefsGroup>
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const running = s.enabled && s.phase !== "stopped";
|
|
106
|
+
const online = s.phase === "online" && s.url !== null;
|
|
107
|
+
const viaThisLink = s.url !== null && sameOrigin(s.url);
|
|
108
|
+
const connecting = running && !online && s.phase !== "error";
|
|
109
|
+
|
|
110
|
+
// Subjudul: satu kalimat pendek yang menjelaskan keadaan + apa artinya.
|
|
111
|
+
let tone: RowTone = "muted";
|
|
112
|
+
let subtitle: ReactNode = "Get a temporary link to open KCG Code on your phone";
|
|
113
|
+
if (!protectedApp) {
|
|
114
|
+
tone = "warning";
|
|
115
|
+
subtitle = "Set a PIN or password first to keep others out";
|
|
116
|
+
} else if (s.phase === "error") {
|
|
117
|
+
tone = "warning";
|
|
118
|
+
subtitle = (
|
|
119
|
+
<span className="text-destructive">
|
|
120
|
+
{s.error
|
|
121
|
+
? apiErrorMessage(s.error)
|
|
122
|
+
: "Couldn't connect. Check your internet and try again."}
|
|
123
|
+
</span>
|
|
124
|
+
);
|
|
125
|
+
} else if (online) {
|
|
126
|
+
tone = "success";
|
|
127
|
+
subtitle = "On · anyone with the link still needs your PIN or password";
|
|
128
|
+
} else if (connecting) {
|
|
129
|
+
tone = "busy";
|
|
130
|
+
subtitle =
|
|
131
|
+
s.phase === "reconnecting"
|
|
132
|
+
? "Reconnecting to localhost.run…"
|
|
133
|
+
: "Getting a link from localhost.run…";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const toggle = (next: boolean) => {
|
|
137
|
+
if (next) void start();
|
|
138
|
+
else onRequestStop(stop, viaThisLink);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
return (
|
|
142
|
+
<section aria-label="localhost.run" className="flex flex-col gap-2">
|
|
143
|
+
<PrefsGroup id="remote">
|
|
144
|
+
{/* AdwSwitchRow: judul + status singkat, switch di kanan. */}
|
|
145
|
+
<li>
|
|
146
|
+
<label
|
|
147
|
+
htmlFor="lhr-switch"
|
|
148
|
+
className={cn(
|
|
149
|
+
"flex min-h-14 items-center gap-3 px-4 py-2.5",
|
|
150
|
+
protectedApp && "cursor-pointer",
|
|
151
|
+
)}
|
|
152
|
+
>
|
|
153
|
+
<RowIcon tone={tone}>
|
|
154
|
+
<GlobeIcon />
|
|
155
|
+
</RowIcon>
|
|
156
|
+
<span className="flex min-w-0 flex-1 flex-col gap-0.5 py-0.5">
|
|
157
|
+
<span className="text-[15px] leading-5">Remote access</span>
|
|
158
|
+
<span role="status" className="text-[13px] leading-snug text-muted-foreground">
|
|
159
|
+
{subtitle}
|
|
160
|
+
</span>
|
|
161
|
+
</span>
|
|
162
|
+
{busy !== null && <Spinner className="size-4 text-muted-foreground" aria-hidden />}
|
|
163
|
+
<Switch
|
|
164
|
+
id="lhr-switch"
|
|
165
|
+
checked={running || busy === "start"}
|
|
166
|
+
disabled={!protectedApp || busy !== null}
|
|
167
|
+
onCheckedChange={toggle}
|
|
168
|
+
/>
|
|
169
|
+
</label>
|
|
170
|
+
</li>
|
|
171
|
+
|
|
172
|
+
{!protectedApp && (
|
|
173
|
+
<ActionRow
|
|
174
|
+
prefix={
|
|
175
|
+
<RowIcon tone="muted">
|
|
176
|
+
<LockIcon />
|
|
177
|
+
</RowIcon>
|
|
178
|
+
}
|
|
179
|
+
title="Set up app lock"
|
|
180
|
+
subtitle="Add a PIN or password in Security"
|
|
181
|
+
onActivate={() => navigate(settingsPath("security"))}
|
|
182
|
+
/>
|
|
183
|
+
)}
|
|
184
|
+
|
|
185
|
+
{online && s.url && <LiveAddress url={s.url} />}
|
|
186
|
+
|
|
187
|
+
{/* Log teknis dilipat di baris expander (AdwExpanderRow). */}
|
|
188
|
+
<Collapsible asChild open={showLog} onOpenChange={setShowLog}>
|
|
189
|
+
<li>
|
|
190
|
+
<CollapsibleTrigger asChild>
|
|
191
|
+
<button
|
|
192
|
+
type="button"
|
|
193
|
+
className="flex min-h-14 w-full items-center gap-3 px-4 py-2.5 text-left text-[15px] leading-5 transition-colors outline-none hover:bg-muted/60 focus-visible:bg-muted/60 focus-visible:ring-[3px] focus-visible:ring-ring/40 focus-visible:ring-inset"
|
|
194
|
+
>
|
|
195
|
+
<RowIcon tone="muted">
|
|
196
|
+
<TerminalSquareIcon />
|
|
197
|
+
</RowIcon>
|
|
198
|
+
<span className="flex min-w-0 flex-1 flex-col gap-0.5">
|
|
199
|
+
<span>Connection log</span>
|
|
200
|
+
<span className="text-[13px] leading-snug text-muted-foreground">
|
|
201
|
+
Technical details for troubleshooting
|
|
202
|
+
</span>
|
|
203
|
+
</span>
|
|
204
|
+
<ChevronDownIcon
|
|
205
|
+
className={cn(
|
|
206
|
+
"size-4 shrink-0 text-muted-foreground transition-transform duration-200",
|
|
207
|
+
showLog && "rotate-180",
|
|
208
|
+
)}
|
|
209
|
+
aria-hidden
|
|
210
|
+
/>
|
|
211
|
+
</button>
|
|
212
|
+
</CollapsibleTrigger>
|
|
213
|
+
<CollapsibleContent className="border-t">
|
|
214
|
+
<ul>{showLog && <LogPanel endpoint="/api/tunnel/lhr/logs" />}</ul>
|
|
215
|
+
</CollapsibleContent>
|
|
216
|
+
</li>
|
|
217
|
+
</Collapsible>
|
|
218
|
+
</PrefsGroup>
|
|
219
|
+
|
|
220
|
+
{/* Footer grup: catatan singkat + aksi inline. */}
|
|
221
|
+
<p className="px-1 text-[12.5px] leading-snug text-muted-foreground">
|
|
222
|
+
Free addresses change every few hours and after reconnecting.{" "}
|
|
223
|
+
<button
|
|
224
|
+
type="button"
|
|
225
|
+
onClick={onUseRecommended}
|
|
226
|
+
className="rounded-sm font-medium text-foreground underline-offset-2 outline-none hover:underline focus-visible:underline focus-visible:ring-[3px] focus-visible:ring-ring/40"
|
|
227
|
+
>
|
|
228
|
+
Use KCG Code link
|
|
229
|
+
</button>{" "}
|
|
230
|
+
for one that stays the same.
|
|
231
|
+
</p>
|
|
232
|
+
</section>
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function sameOrigin(url: string): boolean {
|
|
237
|
+
try {
|
|
238
|
+
return new URL(url).origin === window.location.origin;
|
|
239
|
+
} catch {
|
|
240
|
+
return false;
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
type RowTone = "success" | "busy" | "warning" | "muted";
|
|
245
|
+
|
|
246
|
+
/** Ikon bulat di awal baris (sama dengan baris lain di Settings). */
|
|
247
|
+
function RowIcon({ tone, children }: { tone: RowTone; children: ReactNode }) {
|
|
248
|
+
return (
|
|
249
|
+
<span
|
|
250
|
+
aria-hidden
|
|
251
|
+
className={cn(
|
|
252
|
+
"flex size-9 shrink-0 items-center justify-center rounded-full [&>svg]:size-[18px]",
|
|
253
|
+
tone === "success" && "bg-emerald-500/10 text-emerald-700 dark:text-emerald-400",
|
|
254
|
+
tone === "busy" && "bg-sky-500/10 text-sky-700 dark:text-sky-400",
|
|
255
|
+
tone === "warning" && "bg-amber-500/15 text-amber-800 dark:text-amber-300",
|
|
256
|
+
tone === "muted" && "bg-muted text-muted-foreground",
|
|
257
|
+
)}
|
|
258
|
+
>
|
|
259
|
+
{tone === "busy" ? <Spinner className="size-[18px]" /> : children}
|
|
260
|
+
</span>
|
|
261
|
+
);
|
|
262
|
+
}
|