@hachej/boring-core 0.1.23 → 0.1.26
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/dist/{CoreFront-CgAkiEts.d.ts → CoreFront-N0QJSYaM.d.ts} +4 -1
- package/dist/app/front/index.d.ts +17 -3
- package/dist/app/front/index.js +462 -70
- package/dist/app/server/index.js +6 -5
- package/dist/{chunk-6D7LEQSL.js → chunk-2JDK4XUZ.js} +72 -31
- package/dist/{chunk-JMCBLJ6W.js → chunk-5R3U6QKD.js} +174 -81
- package/dist/front/index.d.ts +33 -3
- package/dist/front/index.js +5 -3
- package/dist/server/index.js +1 -1
- package/package.json +7 -6
|
@@ -14,7 +14,10 @@ interface CoreFrontProps {
|
|
|
14
14
|
children?: ReactNode;
|
|
15
15
|
authPages?: CoreFrontAuthPagesOverride;
|
|
16
16
|
cspNonce?: string;
|
|
17
|
+
workspaceRoute?: string;
|
|
18
|
+
workspaceIdParam?: string;
|
|
19
|
+
publicPaths?: string[];
|
|
17
20
|
}
|
|
18
|
-
declare function CoreFront({ children, authPages, cspNonce }: CoreFrontProps): react_jsx_runtime.JSX.Element;
|
|
21
|
+
declare function CoreFront({ children, authPages, cspNonce, workspaceRoute, workspaceIdParam, publicPaths }: CoreFrontProps): react_jsx_runtime.JSX.Element;
|
|
19
22
|
|
|
20
23
|
export { type CoreFrontAuthPagesOverride as C, CoreFront as a, type CoreFrontProps as b };
|
|
@@ -1,11 +1,25 @@
|
|
|
1
1
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
2
|
import { ReactNode } from 'react';
|
|
3
|
-
import { C as CoreFrontAuthPagesOverride } from '../../CoreFront-
|
|
3
|
+
import { C as CoreFrontAuthPagesOverride } from '../../CoreFront-N0QJSYaM.js';
|
|
4
4
|
import { WorkspaceAgentSession, WorkspaceAgentFrontProps } from '@hachej/boring-workspace/app/front';
|
|
5
|
+
import { ChatSuggestion } from '@hachej/boring-agent/front';
|
|
5
6
|
|
|
7
|
+
interface ChatFirstPublicShellOptions {
|
|
8
|
+
composerPlaceholder?: string;
|
|
9
|
+
emptyState?: {
|
|
10
|
+
eyebrow?: string;
|
|
11
|
+
title?: string;
|
|
12
|
+
description?: string;
|
|
13
|
+
};
|
|
14
|
+
suggestions?: ChatSuggestion[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type ChatEntryMode = 'auth-first' | 'chat-first';
|
|
6
18
|
interface CoreWorkspaceAgentFrontProps<TSession extends WorkspaceAgentSession = WorkspaceAgentSession> extends Omit<WorkspaceAgentFrontProps<TSession>, 'workspaceId' | 'frontPluginHotReload' | 'hotReloadEnabled'> {
|
|
7
19
|
/** Core consumes plugins statically for now; app-level hot reload is explicitly unsupported. */
|
|
8
20
|
hotReload?: false;
|
|
21
|
+
chatEntryMode?: ChatEntryMode;
|
|
22
|
+
chatFirstPublicShell?: ChatFirstPublicShellOptions;
|
|
9
23
|
authPages?: CoreFrontAuthPagesOverride;
|
|
10
24
|
cspNonce?: string;
|
|
11
25
|
children?: ReactNode;
|
|
@@ -15,6 +29,6 @@ interface CoreWorkspaceAgentFrontProps<TSession extends WorkspaceAgentSession =
|
|
|
15
29
|
loadingFallback?: ReactNode;
|
|
16
30
|
bootPreloadPaths?: string[];
|
|
17
31
|
}
|
|
18
|
-
declare function CoreWorkspaceAgentFront<TSession extends WorkspaceAgentSession = WorkspaceAgentSession>({ authPages, cspNonce, children, workspaceRoute, workspaceIdParam, workspaceHref, loadingFallback, bootPreloadPaths, topBarLeft, topBarRight, appTitle, bridgeEndpoint, hotReload, ...workspaceProps }: CoreWorkspaceAgentFrontProps<TSession>): react_jsx_runtime.JSX.Element;
|
|
32
|
+
declare function CoreWorkspaceAgentFront<TSession extends WorkspaceAgentSession = WorkspaceAgentSession>({ authPages, cspNonce, children, workspaceRoute, workspaceIdParam, workspaceHref, loadingFallback, bootPreloadPaths, topBarLeft, topBarRight, appTitle, bridgeEndpoint, hotReload, chatEntryMode, chatFirstPublicShell, ...workspaceProps }: CoreWorkspaceAgentFrontProps<TSession>): react_jsx_runtime.JSX.Element;
|
|
19
33
|
|
|
20
|
-
export { CoreWorkspaceAgentFront, type CoreWorkspaceAgentFrontProps };
|
|
34
|
+
export { type ChatFirstPublicShellOptions, CoreWorkspaceAgentFront, type CoreWorkspaceAgentFrontProps };
|
package/dist/app/front/index.js
CHANGED
|
@@ -2,69 +2,416 @@ import {
|
|
|
2
2
|
CoreFront,
|
|
3
3
|
UserMenu,
|
|
4
4
|
WorkspaceSwitcher,
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
routes,
|
|
6
|
+
useCurrentWorkspace,
|
|
7
|
+
useSession,
|
|
8
|
+
useSignIn,
|
|
9
|
+
useSignUp,
|
|
10
|
+
useWorkspaceRouteStatus
|
|
11
|
+
} from "../../chunk-5R3U6QKD.js";
|
|
7
12
|
import "../../chunk-HYNKZSTF.js";
|
|
8
13
|
import "../../chunk-H5KU6R6Y.js";
|
|
9
14
|
import "../../chunk-MLKGABMK.js";
|
|
10
15
|
|
|
11
16
|
// src/app/front/CoreWorkspaceAgentFront.tsx
|
|
12
|
-
import { useMemo } from "react";
|
|
13
|
-
import { Navigate, Route, useParams } from "react-router-dom";
|
|
17
|
+
import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
|
|
18
|
+
import { Navigate, Route, useLocation as useLocation2, useParams } from "react-router-dom";
|
|
14
19
|
import {
|
|
15
|
-
WorkspaceAgentFront
|
|
16
|
-
WorkspaceBootGate
|
|
20
|
+
WorkspaceAgentFront as WorkspaceAgentFront2
|
|
17
21
|
} from "@hachej/boring-workspace/app/front";
|
|
22
|
+
|
|
23
|
+
// src/app/front/chatFirst/ChatFirstAuthenticatedShell.tsx
|
|
24
|
+
import { useEffect } from "react";
|
|
25
|
+
import {
|
|
26
|
+
WorkspaceAgentFront
|
|
27
|
+
} from "@hachej/boring-workspace/app/front";
|
|
28
|
+
import { useWorkspaceAttention } from "@hachej/boring-workspace";
|
|
18
29
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
30
|
+
function ChatFirstComposerBlocker() {
|
|
31
|
+
const { addBlocker, removeBlocker } = useWorkspaceAttention();
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
const blocker = {
|
|
34
|
+
id: "chat-first-workspace-preparing",
|
|
35
|
+
reason: "workspace-preparing",
|
|
36
|
+
label: "Preparing workspace\u2026 Send will work in a moment."
|
|
37
|
+
};
|
|
38
|
+
addBlocker(blocker);
|
|
39
|
+
return () => removeBlocker(blocker.id);
|
|
40
|
+
}, [addBlocker, removeBlocker]);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
function ChatFirstAuthenticatedShell({
|
|
44
|
+
appTitle,
|
|
45
|
+
workspaceId,
|
|
46
|
+
initialDraft,
|
|
47
|
+
autoSubmitInitialDraft = false,
|
|
48
|
+
workspaceProps,
|
|
49
|
+
showComposerBlocker = true
|
|
50
|
+
}) {
|
|
51
|
+
return /* @__PURE__ */ jsx(
|
|
52
|
+
WorkspaceAgentFront,
|
|
53
|
+
{
|
|
54
|
+
...workspaceProps,
|
|
55
|
+
workspaceId,
|
|
56
|
+
appTitle,
|
|
57
|
+
topBarLeft: null,
|
|
58
|
+
sessions: [],
|
|
59
|
+
activeSessionId: null,
|
|
60
|
+
onSwitchSession: () => void 0,
|
|
61
|
+
onCreateSession: () => void 0,
|
|
62
|
+
onDeleteSession: () => void 0,
|
|
63
|
+
provisionWorkspace: false,
|
|
64
|
+
bootPreloadPaths: [],
|
|
65
|
+
bridgeEndpoint: null,
|
|
66
|
+
excludeDefaults: ["filesystem"],
|
|
67
|
+
plugins: [],
|
|
68
|
+
catalogs: [],
|
|
69
|
+
commands: [],
|
|
70
|
+
persistenceEnabled: false,
|
|
71
|
+
navEnabled: false,
|
|
72
|
+
defaultNavOpen: false,
|
|
73
|
+
defaultSurfaceOpen: false,
|
|
74
|
+
beforeShell: showComposerBlocker ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
75
|
+
workspaceProps.beforeShell,
|
|
76
|
+
/* @__PURE__ */ jsx(ChatFirstComposerBlocker, {})
|
|
77
|
+
] }) : workspaceProps.beforeShell,
|
|
78
|
+
chatParams: {
|
|
79
|
+
...workspaceProps.chatParams,
|
|
80
|
+
composerBlockers: void 0,
|
|
81
|
+
...initialDraft ? { initialDraft } : {},
|
|
82
|
+
...initialDraft && autoSubmitInitialDraft ? { autoSubmitInitialDraft: true } : {},
|
|
83
|
+
serverResourcesEnabled: false,
|
|
84
|
+
hydrateMessages: false,
|
|
85
|
+
onBeforeSubmit: showComposerBlocker ? (() => false) : workspaceProps.chatParams?.onBeforeSubmit ?? (() => false)
|
|
86
|
+
},
|
|
87
|
+
frontPluginHotReload: false,
|
|
88
|
+
hotReloadEnabled: false
|
|
89
|
+
}
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// src/app/front/chatFirst/ChatFirstPublicShell.tsx
|
|
94
|
+
import { useState as useState2 } from "react";
|
|
95
|
+
import { useLocation } from "react-router-dom";
|
|
96
|
+
|
|
97
|
+
// src/app/front/chatFirst/AuthCard.tsx
|
|
98
|
+
import { useState } from "react";
|
|
99
|
+
import { useNavigate } from "react-router-dom";
|
|
100
|
+
import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
101
|
+
function AuthCard({
|
|
102
|
+
returnTo,
|
|
103
|
+
onClose
|
|
104
|
+
}) {
|
|
105
|
+
const navigate = useNavigate();
|
|
106
|
+
const signIn = useSignIn();
|
|
107
|
+
const signUp = useSignUp();
|
|
108
|
+
const [mode, setMode] = useState("signin");
|
|
109
|
+
const [name, setName] = useState("");
|
|
110
|
+
const [email, setEmail] = useState("");
|
|
111
|
+
const [password, setPassword] = useState("");
|
|
112
|
+
const [error, setError] = useState(null);
|
|
113
|
+
const [submitting, setSubmitting] = useState(false);
|
|
114
|
+
const forgotPasswordHref = `${routes.forgotPassword}?redirect=${encodeURIComponent(returnTo)}`;
|
|
115
|
+
async function submit(event) {
|
|
116
|
+
event.preventDefault();
|
|
117
|
+
setError(null);
|
|
118
|
+
setSubmitting(true);
|
|
119
|
+
try {
|
|
120
|
+
const result = mode === "signin" ? await signIn.email({ email, password }) : await signUp.email({ email, password, name: name || email });
|
|
121
|
+
if (result.error) {
|
|
122
|
+
setError(result.error.message ?? `${mode === "signin" ? "Sign in" : "Sign up"} failed`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
onClose?.();
|
|
126
|
+
navigate(returnTo, { replace: true });
|
|
127
|
+
} catch (err) {
|
|
128
|
+
setError(err instanceof Error ? err.message : `${mode === "signin" ? "Sign in" : "Sign up"} failed`);
|
|
129
|
+
} finally {
|
|
130
|
+
setSubmitting(false);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-sm rounded-2xl border border-border bg-card p-4 shadow-2xl", children: [
|
|
134
|
+
onClose ? /* @__PURE__ */ jsx2("div", { className: "mb-3 flex justify-end", children: /* @__PURE__ */ jsx2("button", { type: "button", className: "rounded-full px-2 py-1 text-sm text-muted-foreground hover:bg-muted", onClick: onClose, "aria-label": "Close sign in", children: "\xD7" }) }) : null,
|
|
135
|
+
/* @__PURE__ */ jsx2("h2", { id: "auth-modal-title", className: "text-center text-xl font-semibold", children: mode === "signin" ? "Sign in to continue" : "Create your account" }),
|
|
136
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-2 text-center text-sm text-muted-foreground", children: "Keep your draft and unlock the full workspace." }),
|
|
137
|
+
/* @__PURE__ */ jsxs2("div", { className: "mt-4 grid grid-cols-2 rounded-xl bg-muted p-1 text-sm", children: [
|
|
138
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: `rounded-lg px-3 py-2 ${mode === "signin" ? "bg-background shadow-sm" : "text-muted-foreground"}`, onClick: () => setMode("signin"), children: "Sign in" }),
|
|
139
|
+
/* @__PURE__ */ jsx2("button", { type: "button", className: `rounded-lg px-3 py-2 ${mode === "signup" ? "bg-background shadow-sm" : "text-muted-foreground"}`, onClick: () => setMode("signup"), children: "Sign up" })
|
|
140
|
+
] }),
|
|
141
|
+
/* @__PURE__ */ jsxs2("form", { className: "mt-4 space-y-2.5", onSubmit: submit, children: [
|
|
142
|
+
error ? /* @__PURE__ */ jsx2("div", { className: "rounded-lg border border-destructive/30 bg-destructive/10 p-2 text-sm text-destructive", role: "alert", children: error }) : null,
|
|
143
|
+
mode === "signup" ? /* @__PURE__ */ jsx2("input", { className: "w-full rounded-xl border border-border bg-background px-3 py-2.5 text-sm outline-none focus:border-ring", placeholder: "Name", value: name, onChange: (event) => setName(event.currentTarget.value) }) : null,
|
|
144
|
+
/* @__PURE__ */ jsx2("input", { className: "w-full rounded-xl border border-border bg-background px-3 py-2.5 text-sm outline-none focus:border-ring", type: "email", autoComplete: "email", placeholder: "Email", value: email, onChange: (event) => setEmail(event.currentTarget.value), required: true }),
|
|
145
|
+
/* @__PURE__ */ jsx2("input", { className: "w-full rounded-xl border border-border bg-background px-3 py-2.5 text-sm outline-none focus:border-ring", type: "password", autoComplete: mode === "signin" ? "current-password" : "new-password", placeholder: "Password", value: password, onChange: (event) => setPassword(event.currentTarget.value), required: true }),
|
|
146
|
+
mode === "signin" ? /* @__PURE__ */ jsx2("div", { className: "flex justify-end", children: /* @__PURE__ */ jsx2("a", { href: forgotPasswordHref, className: "text-xs text-muted-foreground hover:underline", children: "Forgot password?" }) }) : null,
|
|
147
|
+
/* @__PURE__ */ jsx2("button", { type: "submit", className: "w-full rounded-xl bg-primary px-3 py-2.5 text-sm font-medium text-primary-foreground disabled:opacity-50", disabled: submitting, children: submitting ? "Please wait\u2026" : mode === "signin" ? "Continue with email" : "Create account" })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ jsx2("p", { className: "mt-4 text-center text-xs text-muted-foreground", children: "By continuing, you agree to continue into your private workspace." })
|
|
150
|
+
] });
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/app/front/chatFirst/AuthModal.tsx
|
|
154
|
+
import { jsx as jsx3 } from "react/jsx-runtime";
|
|
155
|
+
function AuthModal({ onClose, returnTo }) {
|
|
156
|
+
return /* @__PURE__ */ jsx3("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-background/70 p-4 backdrop-blur-md", role: "dialog", "aria-modal": "true", "aria-labelledby": "auth-modal-title", children: /* @__PURE__ */ jsx3(AuthCard, { returnTo, onClose }) });
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// src/app/front/chatFirst/pendingChatEntry.ts
|
|
160
|
+
import { matchPath } from "react-router-dom";
|
|
161
|
+
var PENDING_CHAT_ENTRY_KEY = "boring:pending-chat-entry";
|
|
162
|
+
var PENDING_CHAT_ENTRY_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
163
|
+
var DEFAULT_CHAT_FIRST_PENDING_WORKSPACE_ID = "pending";
|
|
164
|
+
var PENDING_CHAT_ENTRY_CHANGED_EVENT = "boring:pending-chat-entry-changed";
|
|
165
|
+
function browserSessionStorage() {
|
|
166
|
+
try {
|
|
167
|
+
return typeof window === "undefined" ? null : window.sessionStorage;
|
|
168
|
+
} catch {
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
function safeReturnTo(pathname, search, hash) {
|
|
173
|
+
const candidate = `${pathname || "/"}${search || ""}${hash || ""}`;
|
|
174
|
+
if (!candidate.startsWith("/") || candidate.startsWith("//") || /[\0\r\n<>"'`]/.test(candidate)) return "/";
|
|
175
|
+
return candidate;
|
|
176
|
+
}
|
|
177
|
+
function readPendingChatEntry() {
|
|
178
|
+
const storage = browserSessionStorage();
|
|
179
|
+
if (!storage) return null;
|
|
180
|
+
try {
|
|
181
|
+
const raw = storage.getItem(PENDING_CHAT_ENTRY_KEY);
|
|
182
|
+
if (!raw) return null;
|
|
183
|
+
const parsed = JSON.parse(raw);
|
|
184
|
+
if (typeof parsed.draft !== "string" || typeof parsed.returnTo !== "string" || typeof parsed.createdAt !== "number") return null;
|
|
185
|
+
if (Date.now() - parsed.createdAt > PENDING_CHAT_ENTRY_TTL_MS) {
|
|
186
|
+
storage.removeItem(PENDING_CHAT_ENTRY_KEY);
|
|
187
|
+
return null;
|
|
188
|
+
}
|
|
189
|
+
return {
|
|
190
|
+
draft: parsed.draft,
|
|
191
|
+
returnTo: parsed.returnTo,
|
|
192
|
+
intendedWorkspaceId: typeof parsed.intendedWorkspaceId === "string" ? parsed.intendedWorkspaceId : void 0,
|
|
193
|
+
createdAt: parsed.createdAt
|
|
194
|
+
};
|
|
195
|
+
} catch {
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function notifyPendingChatEntryChanged() {
|
|
200
|
+
globalThis.dispatchEvent?.(new Event(PENDING_CHAT_ENTRY_CHANGED_EVENT));
|
|
201
|
+
}
|
|
202
|
+
function writePendingChatEntry(input) {
|
|
203
|
+
const storage = browserSessionStorage();
|
|
204
|
+
if (!storage) return;
|
|
205
|
+
storage.setItem(PENDING_CHAT_ENTRY_KEY, JSON.stringify({ ...input, createdAt: Date.now() }));
|
|
206
|
+
notifyPendingChatEntryChanged();
|
|
207
|
+
}
|
|
208
|
+
function clearPendingChatEntry() {
|
|
209
|
+
browserSessionStorage()?.removeItem(PENDING_CHAT_ENTRY_KEY);
|
|
210
|
+
notifyPendingChatEntryChanged();
|
|
211
|
+
}
|
|
212
|
+
function pendingChatEntryMatchesLocation(pending, pathname, search, hash) {
|
|
213
|
+
return Boolean(pending && pending.returnTo === safeReturnTo(pathname, search, hash));
|
|
214
|
+
}
|
|
215
|
+
function routePatterns(route) {
|
|
216
|
+
const normalized = route.endsWith("/*") ? route.slice(0, -2) : route;
|
|
217
|
+
return [`${normalized}/*`, normalized];
|
|
218
|
+
}
|
|
219
|
+
function workspaceIdFromPath(pathname, workspaceRoute, workspaceIdParam) {
|
|
220
|
+
const patterns = [
|
|
221
|
+
...routePatterns(workspaceRoute),
|
|
222
|
+
"/w/:id/*",
|
|
223
|
+
"/w/:id",
|
|
224
|
+
"/workspace/:id/*",
|
|
225
|
+
"/workspace/:id"
|
|
226
|
+
];
|
|
227
|
+
for (const pattern of patterns) {
|
|
228
|
+
const match = matchPath(pattern, pathname);
|
|
229
|
+
const id = match?.params[workspaceIdParam]?.trim() ?? match?.params.id?.trim();
|
|
230
|
+
if (id) return id;
|
|
231
|
+
}
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// src/app/front/chatFirst/ChatFirstPublicShell.tsx
|
|
236
|
+
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
237
|
+
var defaultPublicEmptyState = {
|
|
238
|
+
eyebrow: "Start here",
|
|
239
|
+
title: "What do you want to build?",
|
|
240
|
+
description: "Type a prompt or pick an example. Sign in on send to unlock your private workspace."
|
|
241
|
+
};
|
|
242
|
+
var defaultPublicSuggestions = [
|
|
243
|
+
{ label: "Build an app from scratch", hint: "Creates files, installs deps, opens a preview", prompt: "Build a full-stack app with auth, a dashboard, and sample data." },
|
|
244
|
+
{ label: "Understand a codebase", hint: "Maps the repo and explains where to start", prompt: "Explain this codebase, map the architecture, and suggest first improvements." },
|
|
245
|
+
{ label: "Fix a bug safely", hint: "Finds the cause, edits files, runs tests", prompt: "Trace a bug, edit the right files, update tests, and summarize the diff." },
|
|
246
|
+
{ label: "Prototype an interface", hint: "Turns an idea into an interactive UI", prompt: "Build an interactive prototype and open it in the workspace." }
|
|
247
|
+
];
|
|
248
|
+
function readComposerDraftFromDom() {
|
|
249
|
+
if (typeof document === "undefined") return "";
|
|
250
|
+
const input = document.querySelector('[data-boring-agent-part="composer-input"]');
|
|
251
|
+
return input?.value ?? "";
|
|
252
|
+
}
|
|
253
|
+
function ChatFirstPublicShell({
|
|
254
|
+
appTitle,
|
|
255
|
+
intendedWorkspaceId,
|
|
256
|
+
publicShell,
|
|
257
|
+
workspaceProps
|
|
258
|
+
}) {
|
|
259
|
+
const location = useLocation();
|
|
260
|
+
const [modalOpen, setModalOpen] = useState2(false);
|
|
261
|
+
const returnTo = safeReturnTo(location.pathname, location.search, location.hash);
|
|
262
|
+
const workspaceId = intendedWorkspaceId || "public";
|
|
263
|
+
const openAuth = (draft = readComposerDraftFromDom()) => {
|
|
264
|
+
writePendingChatEntry({ draft, returnTo, ...intendedWorkspaceId ? { intendedWorkspaceId } : {} });
|
|
265
|
+
setModalOpen(true);
|
|
266
|
+
};
|
|
267
|
+
return /* @__PURE__ */ jsxs3("div", { className: "relative h-screen min-h-0", children: [
|
|
268
|
+
/* @__PURE__ */ jsx4(
|
|
269
|
+
ChatFirstAuthenticatedShell,
|
|
270
|
+
{
|
|
271
|
+
appTitle,
|
|
272
|
+
workspaceId,
|
|
273
|
+
showComposerBlocker: false,
|
|
274
|
+
workspaceProps: {
|
|
275
|
+
...workspaceProps,
|
|
276
|
+
topBarRight: /* @__PURE__ */ jsx4("button", { type: "button", className: "rounded-md border border-border px-3 py-1.5 text-sm font-medium hover:bg-muted", onClick: () => openAuth(), children: "Sign in" }),
|
|
277
|
+
className: workspaceProps.className,
|
|
278
|
+
surfaceButtonBottomOffset: 456,
|
|
279
|
+
chatParams: {
|
|
280
|
+
...workspaceProps.chatParams,
|
|
281
|
+
emptyPlacement: "hero",
|
|
282
|
+
composerPlaceholder: publicShell?.composerPlaceholder ?? "Describe the app, bug, or repo task you want help with\u2026",
|
|
283
|
+
emptyState: {
|
|
284
|
+
...defaultPublicEmptyState,
|
|
285
|
+
...publicShell?.emptyState
|
|
286
|
+
},
|
|
287
|
+
suggestions: publicShell?.suggestions ?? defaultPublicSuggestions,
|
|
288
|
+
onBeforeSubmit: (draft) => {
|
|
289
|
+
openAuth(draft);
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
),
|
|
296
|
+
/* @__PURE__ */ jsx4("aside", { className: "pointer-events-none fixed bottom-6 right-6 z-20 w-[320px]", children: /* @__PURE__ */ jsx4("div", { className: "pointer-events-auto", children: /* @__PURE__ */ jsx4(AuthCard, { returnTo }) }) }),
|
|
297
|
+
modalOpen ? /* @__PURE__ */ jsx4(AuthModal, { returnTo, onClose: () => setModalOpen(false) }) : null
|
|
298
|
+
] });
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// src/app/front/CoreWorkspaceAgentFront.tsx
|
|
302
|
+
import { Fragment as Fragment2, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
19
303
|
var DEFAULT_WORKSPACE_ROUTE = "/workspace/:id";
|
|
20
304
|
var DEFAULT_WORKSPACE_ID_PARAM = "id";
|
|
21
305
|
function DefaultTopBarRight() {
|
|
22
|
-
return /* @__PURE__ */
|
|
306
|
+
return /* @__PURE__ */ jsx5(UserMenu, {});
|
|
23
307
|
}
|
|
24
308
|
function WorkspaceLoadingPage({
|
|
25
309
|
appTitle,
|
|
26
310
|
topBarLeft,
|
|
27
311
|
topBarRight
|
|
28
312
|
}) {
|
|
29
|
-
return /* @__PURE__ */
|
|
30
|
-
/* @__PURE__ */
|
|
31
|
-
/* @__PURE__ */
|
|
32
|
-
/* @__PURE__ */
|
|
313
|
+
return /* @__PURE__ */ jsxs4("div", { className: "flex h-screen min-h-0 flex-col bg-background text-foreground", children: [
|
|
314
|
+
/* @__PURE__ */ jsxs4("header", { className: "flex h-12 shrink-0 items-center justify-between border-b border-border bg-card px-3", children: [
|
|
315
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-w-0 items-center gap-3", children: [
|
|
316
|
+
/* @__PURE__ */ jsx5("div", { className: "text-sm font-semibold", children: appTitle }),
|
|
33
317
|
topBarLeft
|
|
34
318
|
] }),
|
|
35
319
|
topBarRight
|
|
36
320
|
] }),
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
/* @__PURE__ */
|
|
321
|
+
/* @__PURE__ */ jsx5("main", { className: "flex min-h-0 flex-1 items-center justify-center px-6", children: /* @__PURE__ */ jsxs4("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-6 text-center shadow-sm", children: [
|
|
322
|
+
/* @__PURE__ */ jsx5(
|
|
39
323
|
"div",
|
|
40
324
|
{
|
|
41
325
|
"aria-hidden": "true",
|
|
42
326
|
className: "mx-auto mb-4 h-8 w-8 rounded-full border-2 border-muted-foreground/20 border-t-foreground animate-spin will-change-transform"
|
|
43
327
|
}
|
|
44
328
|
),
|
|
45
|
-
/* @__PURE__ */
|
|
46
|
-
/* @__PURE__ */
|
|
47
|
-
/* @__PURE__ */
|
|
329
|
+
/* @__PURE__ */ jsx5("h1", { className: "text-lg font-semibold", children: "Switching workspace" }),
|
|
330
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-2 text-sm text-muted-foreground", children: "Restoring files, sessions, and saved layout." }),
|
|
331
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-4 text-xs uppercase tracking-[0.18em] text-muted-foreground", children: "Loading workspace" })
|
|
48
332
|
] }) })
|
|
49
333
|
] });
|
|
50
334
|
}
|
|
335
|
+
function usePendingChatDraft() {
|
|
336
|
+
const session = useSession();
|
|
337
|
+
const userId = session.data?.user?.id ?? null;
|
|
338
|
+
const [pending, setPending] = useState3(() => userId ? readPendingChatEntry() : null);
|
|
339
|
+
useEffect2(() => {
|
|
340
|
+
if (!userId) {
|
|
341
|
+
setPending(null);
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
setPending(readPendingChatEntry());
|
|
345
|
+
const syncPending = () => setPending(readPendingChatEntry());
|
|
346
|
+
globalThis.addEventListener?.(PENDING_CHAT_ENTRY_CHANGED_EVENT, syncPending);
|
|
347
|
+
return () => globalThis.removeEventListener?.(PENDING_CHAT_ENTRY_CHANGED_EVENT, syncPending);
|
|
348
|
+
}, [userId]);
|
|
349
|
+
return pending;
|
|
350
|
+
}
|
|
51
351
|
function HomeRedirect({
|
|
52
352
|
loadingFallback,
|
|
53
|
-
workspaceHref
|
|
353
|
+
workspaceHref,
|
|
354
|
+
chatEntryMode,
|
|
355
|
+
appTitle,
|
|
356
|
+
workspaceProps,
|
|
357
|
+
chatFirstPublicShell
|
|
54
358
|
}) {
|
|
359
|
+
const location = useLocation2();
|
|
360
|
+
const session = useSession();
|
|
55
361
|
const workspace = useCurrentWorkspace();
|
|
56
|
-
|
|
57
|
-
|
|
362
|
+
const pendingChatEntry = usePendingChatDraft();
|
|
363
|
+
const restorePendingDraft = pendingChatEntryMatchesLocation(
|
|
364
|
+
pendingChatEntry,
|
|
365
|
+
location.pathname,
|
|
366
|
+
location.search,
|
|
367
|
+
location.hash
|
|
368
|
+
);
|
|
369
|
+
if (!session.data?.user && chatEntryMode === "chat-first") return /* @__PURE__ */ jsx5(ChatFirstPublicShell, { appTitle, publicShell: chatFirstPublicShell, workspaceProps });
|
|
370
|
+
if (!workspace && chatEntryMode === "chat-first" && session.data?.user && restorePendingDraft) {
|
|
371
|
+
return /* @__PURE__ */ jsx5(
|
|
372
|
+
ChatFirstAuthenticatedShell,
|
|
373
|
+
{
|
|
374
|
+
appTitle,
|
|
375
|
+
workspaceId: pendingChatEntry?.intendedWorkspaceId ?? DEFAULT_CHAT_FIRST_PENDING_WORKSPACE_ID,
|
|
376
|
+
initialDraft: pendingChatEntry?.draft,
|
|
377
|
+
workspaceProps
|
|
378
|
+
}
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
if (!workspace) return /* @__PURE__ */ jsx5(Fragment2, { children: loadingFallback });
|
|
382
|
+
return /* @__PURE__ */ jsx5(Navigate, { to: workspaceHref(workspace.id), replace: true });
|
|
383
|
+
}
|
|
384
|
+
function WorkspaceRouteErrorPage({ status, message }) {
|
|
385
|
+
const title = status === "not-found" ? "Workspace not found" : status === "forbidden" ? "Workspace unavailable" : "Workspace failed to open";
|
|
386
|
+
return /* @__PURE__ */ jsx5("div", { className: "flex h-screen min-h-0 items-center justify-center bg-background px-6 text-foreground", children: /* @__PURE__ */ jsxs4("div", { className: "w-full max-w-md rounded-2xl border border-border bg-card p-6 text-center shadow-sm", children: [
|
|
387
|
+
/* @__PURE__ */ jsx5("h1", { className: "text-lg font-semibold", children: title }),
|
|
388
|
+
/* @__PURE__ */ jsx5("p", { className: "mt-2 text-sm text-muted-foreground", children: message })
|
|
389
|
+
] }) });
|
|
58
390
|
}
|
|
59
391
|
function WorkspaceRoute({
|
|
60
392
|
workspaceIdParam,
|
|
61
393
|
loadingFallback,
|
|
62
394
|
bootPreloadPaths,
|
|
63
|
-
workspaceProps
|
|
395
|
+
workspaceProps,
|
|
396
|
+
chatEntryMode,
|
|
397
|
+
appTitle,
|
|
398
|
+
workspaceRoute,
|
|
399
|
+
chatFirstPublicShell
|
|
64
400
|
}) {
|
|
65
401
|
const params = useParams();
|
|
402
|
+
const location = useLocation2();
|
|
403
|
+
const session = useSession();
|
|
404
|
+
const pendingChatEntry = usePendingChatDraft();
|
|
66
405
|
const currentWorkspace = useCurrentWorkspace();
|
|
67
|
-
const
|
|
406
|
+
const routeStatus = useWorkspaceRouteStatus();
|
|
407
|
+
const workspaceId = params[workspaceIdParam]?.trim() ?? workspaceIdFromPath(location.pathname, workspaceRoute, workspaceIdParam) ?? "";
|
|
408
|
+
const pendingDraftTargetsWorkspace = !pendingChatEntry?.intendedWorkspaceId || pendingChatEntry.intendedWorkspaceId === workspaceId;
|
|
409
|
+
const restorePendingDraft = pendingDraftTargetsWorkspace && (pendingChatEntryMatchesLocation(
|
|
410
|
+
pendingChatEntry,
|
|
411
|
+
location.pathname,
|
|
412
|
+
location.search,
|
|
413
|
+
location.hash
|
|
414
|
+
) || pendingChatEntry?.returnTo === "/" && currentWorkspace?.id === workspaceId);
|
|
68
415
|
const requestHeaders = useMemo(
|
|
69
416
|
() => ({ ...workspaceProps.requestHeaders, "x-boring-workspace-id": workspaceId }),
|
|
70
417
|
[workspaceId, workspaceProps.requestHeaders]
|
|
@@ -73,30 +420,55 @@ function WorkspaceRoute({
|
|
|
73
420
|
() => ({ ...workspaceProps.authHeaders, "x-boring-workspace-id": workspaceId }),
|
|
74
421
|
[workspaceId, workspaceProps.authHeaders]
|
|
75
422
|
);
|
|
76
|
-
if (!workspaceId) return /* @__PURE__ */
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
|
|
423
|
+
if (!workspaceId) return /* @__PURE__ */ jsx5(Fragment2, { children: loadingFallback });
|
|
424
|
+
if (!session.data?.user && chatEntryMode === "chat-first") {
|
|
425
|
+
return /* @__PURE__ */ jsx5(ChatFirstPublicShell, { appTitle, intendedWorkspaceId: workspaceId, publicShell: chatFirstPublicShell, workspaceProps });
|
|
426
|
+
}
|
|
427
|
+
if (routeStatus.status === "not-found" || routeStatus.status === "forbidden" || routeStatus.status === "switch-failed") {
|
|
428
|
+
return /* @__PURE__ */ jsx5(WorkspaceRouteErrorPage, { status: routeStatus.status, message: routeStatus.message });
|
|
429
|
+
}
|
|
430
|
+
if (chatEntryMode === "chat-first" && restorePendingDraft && (routeStatus.status !== "matched" || currentWorkspace?.id !== workspaceId)) {
|
|
431
|
+
return /* @__PURE__ */ jsx5(
|
|
432
|
+
ChatFirstAuthenticatedShell,
|
|
433
|
+
{
|
|
434
|
+
appTitle,
|
|
435
|
+
workspaceId,
|
|
436
|
+
initialDraft: pendingChatEntry?.draft,
|
|
437
|
+
workspaceProps
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
if (routeStatus.status !== "matched" || currentWorkspace?.id !== workspaceId) return /* @__PURE__ */ jsx5(Fragment2, { children: loadingFallback });
|
|
442
|
+
const shouldRestorePendingDraft = restorePendingDraft && Boolean(pendingChatEntry?.draft);
|
|
443
|
+
const chatParams = {
|
|
444
|
+
...workspaceProps.chatParams,
|
|
445
|
+
...shouldRestorePendingDraft ? { initialDraft: pendingChatEntry?.draft } : {},
|
|
446
|
+
...shouldRestorePendingDraft ? { autoSubmitInitialDraft: true } : {},
|
|
447
|
+
onBeforeSubmit: async (draft, ctx) => {
|
|
448
|
+
const existing = workspaceProps.chatParams?.onBeforeSubmit;
|
|
449
|
+
const result = await existing?.(draft, ctx);
|
|
450
|
+
if (result !== false) clearPendingChatEntry();
|
|
451
|
+
return result;
|
|
452
|
+
}
|
|
453
|
+
};
|
|
454
|
+
return /* @__PURE__ */ jsx5(
|
|
455
|
+
WorkspaceAgentFront2,
|
|
80
456
|
{
|
|
457
|
+
...workspaceProps,
|
|
81
458
|
workspaceId,
|
|
82
459
|
requestHeaders,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
workspaceId,
|
|
91
|
-
requestHeaders,
|
|
92
|
-
authHeaders,
|
|
93
|
-
frontPluginHotReload: false,
|
|
94
|
-
hotReloadEnabled: false
|
|
95
|
-
}
|
|
96
|
-
)
|
|
97
|
-
}
|
|
460
|
+
authHeaders,
|
|
461
|
+
chatParams,
|
|
462
|
+
bootPreloadPaths,
|
|
463
|
+
frontPluginHotReload: false,
|
|
464
|
+
hotReloadEnabled: false
|
|
465
|
+
},
|
|
466
|
+
workspaceId
|
|
98
467
|
);
|
|
99
468
|
}
|
|
469
|
+
function chatFirstPublicPaths(workspaceRoute) {
|
|
470
|
+
return Array.from(/* @__PURE__ */ new Set(["/", workspaceRoute, "/workspace/:id", "/w/:id"]));
|
|
471
|
+
}
|
|
100
472
|
function CoreWorkspaceAgentFront({
|
|
101
473
|
authPages,
|
|
102
474
|
cspNonce,
|
|
@@ -106,11 +478,13 @@ function CoreWorkspaceAgentFront({
|
|
|
106
478
|
workspaceHref = (workspaceId) => `/workspace/${workspaceId}`,
|
|
107
479
|
loadingFallback,
|
|
108
480
|
bootPreloadPaths,
|
|
109
|
-
topBarLeft = /* @__PURE__ */
|
|
110
|
-
topBarRight = /* @__PURE__ */
|
|
481
|
+
topBarLeft = /* @__PURE__ */ jsx5(WorkspaceSwitcher, {}),
|
|
482
|
+
topBarRight = /* @__PURE__ */ jsx5(DefaultTopBarRight, {}),
|
|
111
483
|
appTitle = "Boring",
|
|
112
484
|
bridgeEndpoint = "/api/v1/ui",
|
|
113
485
|
hotReload = false,
|
|
486
|
+
chatEntryMode = "auth-first",
|
|
487
|
+
chatFirstPublicShell,
|
|
114
488
|
...workspaceProps
|
|
115
489
|
}) {
|
|
116
490
|
if (hotReload !== false) {
|
|
@@ -118,7 +492,7 @@ function CoreWorkspaceAgentFront({
|
|
|
118
492
|
"CoreWorkspaceAgentFront does not support hotReload yet; use static plugin consumption or WorkspaceAgentFront for standalone hot reload."
|
|
119
493
|
);
|
|
120
494
|
}
|
|
121
|
-
const resolvedLoadingFallback = loadingFallback ?? /* @__PURE__ */
|
|
495
|
+
const resolvedLoadingFallback = loadingFallback ?? /* @__PURE__ */ jsx5(
|
|
122
496
|
WorkspaceLoadingPage,
|
|
123
497
|
{
|
|
124
498
|
appTitle,
|
|
@@ -133,37 +507,55 @@ function CoreWorkspaceAgentFront({
|
|
|
133
507
|
topBarRight,
|
|
134
508
|
bridgeEndpoint
|
|
135
509
|
};
|
|
136
|
-
return /* @__PURE__ */
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
510
|
+
return /* @__PURE__ */ jsxs4(
|
|
511
|
+
CoreFront,
|
|
512
|
+
{
|
|
513
|
+
authPages,
|
|
514
|
+
cspNonce,
|
|
515
|
+
workspaceRoute,
|
|
516
|
+
workspaceIdParam,
|
|
517
|
+
publicPaths: chatEntryMode === "chat-first" ? chatFirstPublicPaths(workspaceRoute) : void 0,
|
|
518
|
+
children: [
|
|
519
|
+
/* @__PURE__ */ jsx5(
|
|
520
|
+
Route,
|
|
143
521
|
{
|
|
144
|
-
|
|
145
|
-
|
|
522
|
+
path: "/",
|
|
523
|
+
element: /* @__PURE__ */ jsx5(
|
|
524
|
+
HomeRedirect,
|
|
525
|
+
{
|
|
526
|
+
loadingFallback: resolvedLoadingFallback,
|
|
527
|
+
workspaceHref,
|
|
528
|
+
chatEntryMode,
|
|
529
|
+
appTitle,
|
|
530
|
+
workspaceProps: resolvedWorkspaceProps,
|
|
531
|
+
chatFirstPublicShell
|
|
532
|
+
}
|
|
533
|
+
)
|
|
146
534
|
}
|
|
147
|
-
)
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
/* @__PURE__ */ jsx(
|
|
151
|
-
Route,
|
|
152
|
-
{
|
|
153
|
-
path: workspaceRoute,
|
|
154
|
-
element: /* @__PURE__ */ jsx(
|
|
155
|
-
WorkspaceRoute,
|
|
535
|
+
),
|
|
536
|
+
/* @__PURE__ */ jsx5(
|
|
537
|
+
Route,
|
|
156
538
|
{
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
539
|
+
path: workspaceRoute,
|
|
540
|
+
element: /* @__PURE__ */ jsx5(
|
|
541
|
+
WorkspaceRoute,
|
|
542
|
+
{
|
|
543
|
+
workspaceIdParam,
|
|
544
|
+
loadingFallback: resolvedLoadingFallback,
|
|
545
|
+
bootPreloadPaths,
|
|
546
|
+
workspaceProps: resolvedWorkspaceProps,
|
|
547
|
+
chatEntryMode,
|
|
548
|
+
appTitle,
|
|
549
|
+
workspaceRoute,
|
|
550
|
+
chatFirstPublicShell
|
|
551
|
+
}
|
|
552
|
+
)
|
|
161
553
|
}
|
|
162
|
-
)
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
554
|
+
),
|
|
555
|
+
children
|
|
556
|
+
]
|
|
557
|
+
}
|
|
558
|
+
);
|
|
167
559
|
}
|
|
168
560
|
export {
|
|
169
561
|
CoreWorkspaceAgentFront
|