@hachej/boring-core 0.1.59 → 0.1.61
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/app/front/chatFirst/chatFirstPublicShell.css +105 -2
- package/dist/app/front/index.d.ts +10 -0
- package/dist/app/front/index.js +38 -106
- package/dist/app/server/index.js +1 -0
- package/dist/{chunk-Q53N4QUK.js → chunk-NC46VGXS.js} +411 -258
- package/dist/front/index.d.ts +38 -3
- package/dist/front/index.js +3 -1
- package/package.json +4 -4
|
@@ -17,6 +17,109 @@
|
|
|
17
17
|
);
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/* --- Public top-bar CTA + Calendly popover --- */
|
|
21
|
+
.public-topbar-actions {
|
|
22
|
+
display: inline-flex;
|
|
23
|
+
align-items: center;
|
|
24
|
+
gap: 8px;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.public-contact-popover-wrap {
|
|
28
|
+
position: relative;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.public-contact-button,
|
|
32
|
+
.public-sign-in-button {
|
|
33
|
+
border: 1px solid var(--border);
|
|
34
|
+
border-radius: 8px;
|
|
35
|
+
padding: 6px 12px;
|
|
36
|
+
font-size: 13px;
|
|
37
|
+
font-weight: 600;
|
|
38
|
+
line-height: 1.2;
|
|
39
|
+
transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.public-contact-button {
|
|
43
|
+
background: var(--foreground);
|
|
44
|
+
color: var(--background);
|
|
45
|
+
border-color: var(--foreground);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.public-contact-button:hover {
|
|
49
|
+
background: color-mix(in oklch, var(--foreground) 88%, var(--accent));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.public-sign-in-button {
|
|
53
|
+
background: transparent;
|
|
54
|
+
color: var(--foreground);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.public-sign-in-button:hover {
|
|
58
|
+
background: var(--muted);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.public-contact-popover {
|
|
62
|
+
position: absolute;
|
|
63
|
+
top: calc(100% + 10px);
|
|
64
|
+
right: 0;
|
|
65
|
+
z-index: 80;
|
|
66
|
+
width: min(420px, calc(100vw - 24px));
|
|
67
|
+
overflow: hidden;
|
|
68
|
+
border: 1px solid var(--border);
|
|
69
|
+
border-radius: 18px;
|
|
70
|
+
background: var(--background);
|
|
71
|
+
box-shadow: 0 24px 80px color-mix(in oklch, var(--foreground) 18%, transparent);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.public-contact-popover-header {
|
|
75
|
+
display: flex;
|
|
76
|
+
align-items: center;
|
|
77
|
+
justify-content: space-between;
|
|
78
|
+
gap: 12px;
|
|
79
|
+
border-bottom: 1px solid var(--border);
|
|
80
|
+
padding: 10px 12px 10px 14px;
|
|
81
|
+
font-size: 13px;
|
|
82
|
+
font-weight: 700;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.public-contact-popover-header button {
|
|
86
|
+
display: grid;
|
|
87
|
+
width: 28px;
|
|
88
|
+
height: 28px;
|
|
89
|
+
place-items: center;
|
|
90
|
+
border: 0;
|
|
91
|
+
border-radius: 8px;
|
|
92
|
+
background: transparent;
|
|
93
|
+
color: var(--muted-foreground);
|
|
94
|
+
cursor: pointer;
|
|
95
|
+
font-size: 20px;
|
|
96
|
+
line-height: 1;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.public-contact-popover-header button:hover {
|
|
100
|
+
background: var(--muted);
|
|
101
|
+
color: var(--foreground);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.public-contact-popover iframe {
|
|
105
|
+
display: block;
|
|
106
|
+
width: 100%;
|
|
107
|
+
height: min(680px, calc(100vh - 120px));
|
|
108
|
+
min-height: 520px;
|
|
109
|
+
border: 0;
|
|
110
|
+
background: var(--background);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@media (max-width: 560px) {
|
|
114
|
+
.public-contact-popover {
|
|
115
|
+
position: fixed;
|
|
116
|
+
top: 56px;
|
|
117
|
+
right: 8px;
|
|
118
|
+
left: 8px;
|
|
119
|
+
width: auto;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
20
123
|
/* --- Hand-drawn teaching annotations (public no-auth shell) --- */
|
|
21
124
|
.public-arrow {
|
|
22
125
|
pointer-events: none;
|
|
@@ -165,7 +268,7 @@
|
|
|
165
268
|
padding-top: clamp(28px, 9vh, 132px);
|
|
166
269
|
}
|
|
167
270
|
|
|
168
|
-
/* Move
|
|
271
|
+
/* Move suggestion cards to the bottom of the hero block (after the footer),
|
|
169
272
|
so they sit directly above the composer. The empty-state is a flex column,
|
|
170
273
|
so `order` reorders without touching the React tree. */
|
|
171
274
|
.public-chat-first-shell [data-boring-agent-part="empty-state"] .public-hero-foot {
|
|
@@ -175,7 +278,7 @@
|
|
|
175
278
|
order: 20;
|
|
176
279
|
}
|
|
177
280
|
|
|
178
|
-
/*
|
|
281
|
+
/* Suggestion cards — keep example prompts visually connected to the composer. */
|
|
179
282
|
.public-chat-first-shell [data-boring-agent-part="suggestion-grid"] {
|
|
180
283
|
margin-top: 28px;
|
|
181
284
|
max-width: 560px;
|
|
@@ -26,6 +26,16 @@ interface ChatFirstPublicShellOptions {
|
|
|
26
26
|
label: string;
|
|
27
27
|
available?: boolean;
|
|
28
28
|
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Optional public top-bar contact CTA. When set, the no-auth top bar shows a
|
|
31
|
+
* “Get in touch” button that opens a Calendly popover instead of asking users
|
|
32
|
+
* to discover hidden chat commands.
|
|
33
|
+
*/
|
|
34
|
+
contact?: {
|
|
35
|
+
label?: string;
|
|
36
|
+
calendlyUrl: string;
|
|
37
|
+
title?: string;
|
|
38
|
+
};
|
|
29
39
|
/**
|
|
30
40
|
* Hand-drawn "Ask your AI here" / "Review its work here" annotations point at the
|
|
31
41
|
* composer and the workspace surface to teach the empty public shell. Apps
|
package/dist/app/front/index.js
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useSignIn,
|
|
10
10
|
useSignUp,
|
|
11
11
|
useWorkspaceRouteStatus
|
|
12
|
-
} from "../../chunk-
|
|
12
|
+
} from "../../chunk-NC46VGXS.js";
|
|
13
13
|
import "../../chunk-HYNKZSTF.js";
|
|
14
14
|
import {
|
|
15
15
|
isRuntimeEmailVerificationEnabled
|
|
@@ -18,7 +18,7 @@ import "../../chunk-QZGYKLXB.js";
|
|
|
18
18
|
import "../../chunk-MLKGABMK.js";
|
|
19
19
|
|
|
20
20
|
// src/app/front/CoreWorkspaceAgentFront.tsx
|
|
21
|
-
import { useEffect as
|
|
21
|
+
import { useEffect as useEffect2, useMemo, useState as useState3 } from "react";
|
|
22
22
|
import { Navigate, Route, useLocation as useLocation2, useParams } from "react-router-dom";
|
|
23
23
|
import { WorkspaceProvider } from "@hachej/boring-workspace";
|
|
24
24
|
import { ErrorState } from "@hachej/boring-ui-kit";
|
|
@@ -103,10 +103,9 @@ function ChatFirstAuthenticatedShell({
|
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
// src/app/front/chatFirst/ChatFirstPublicShell.tsx
|
|
106
|
-
import {
|
|
106
|
+
import { useState as useState2 } from "react";
|
|
107
107
|
import { useLocation } from "react-router-dom";
|
|
108
108
|
import { builtinCommands } from "@hachej/boring-agent/front";
|
|
109
|
-
import { postUiCommand } from "@hachej/boring-workspace";
|
|
110
109
|
|
|
111
110
|
// src/app/front/chatFirst/AuthCard.tsx
|
|
112
111
|
import { useState } from "react";
|
|
@@ -265,6 +264,25 @@ function readComposerDraftFromDom() {
|
|
|
265
264
|
const input = document.querySelector('[data-boring-agent-part="composer-input"]');
|
|
266
265
|
return input?.value ?? "";
|
|
267
266
|
}
|
|
267
|
+
function PublicTopBarActions({
|
|
268
|
+
contact,
|
|
269
|
+
onSignIn
|
|
270
|
+
}) {
|
|
271
|
+
const [contactOpen, setContactOpen] = useState2(false);
|
|
272
|
+
return /* @__PURE__ */ jsxs3("div", { className: "public-topbar-actions", children: [
|
|
273
|
+
contact ? /* @__PURE__ */ jsxs3("div", { className: "public-contact-popover-wrap", children: [
|
|
274
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "public-contact-button", onClick: () => setContactOpen((open) => !open), children: contact.label ?? "Get in touch" }),
|
|
275
|
+
contactOpen ? /* @__PURE__ */ jsxs3("div", { className: "public-contact-popover", role: "dialog", "aria-label": contact.title ?? "Schedule a call", children: [
|
|
276
|
+
/* @__PURE__ */ jsxs3("div", { className: "public-contact-popover-header", children: [
|
|
277
|
+
/* @__PURE__ */ jsx4("span", { children: contact.title ?? "Schedule a call" }),
|
|
278
|
+
/* @__PURE__ */ jsx4("button", { type: "button", "aria-label": "Close contact popover", onClick: () => setContactOpen(false), children: "\xD7" })
|
|
279
|
+
] }),
|
|
280
|
+
/* @__PURE__ */ jsx4("iframe", { title: contact.title ?? "Schedule a call", src: contact.calendlyUrl, loading: "lazy" })
|
|
281
|
+
] }) : null
|
|
282
|
+
] }) : null,
|
|
283
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: "public-sign-in-button", onClick: onSignIn, children: "Sign in" })
|
|
284
|
+
] });
|
|
285
|
+
}
|
|
268
286
|
function ChatFirstPublicShell({
|
|
269
287
|
appTitle,
|
|
270
288
|
intendedWorkspaceId,
|
|
@@ -273,7 +291,6 @@ function ChatFirstPublicShell({
|
|
|
273
291
|
}) {
|
|
274
292
|
const location = useLocation();
|
|
275
293
|
const [modalOpen, setModalOpen] = useState2(false);
|
|
276
|
-
const lastAutoRunCommandRef = useRef("");
|
|
277
294
|
const returnTo = safeReturnTo(location.pathname, location.search, location.hash);
|
|
278
295
|
const promptedDraft = new URLSearchParams(location.search).get("prompt")?.trim() ?? "";
|
|
279
296
|
const pendingReturnTo = promptedDraft ? "/" : returnTo;
|
|
@@ -284,91 +301,6 @@ function ChatFirstPublicShell({
|
|
|
284
301
|
writePendingChatEntry({ draft, returnTo: pendingReturnTo, ...intendedWorkspaceId ? { intendedWorkspaceId } : {} });
|
|
285
302
|
setModalOpen(true);
|
|
286
303
|
};
|
|
287
|
-
const normalizePublicCommand = (draft) => draft.trim().toLowerCase().replace(/[’`]/g, "'");
|
|
288
|
-
const openLandingPage = () => postUiCommand({
|
|
289
|
-
kind: "openPanel",
|
|
290
|
-
params: { id: "public-landing-page", component: "public.launch.landing", title: "Landing page" }
|
|
291
|
-
});
|
|
292
|
-
const openLetsChat = () => postUiCommand({
|
|
293
|
-
kind: "openPanel",
|
|
294
|
-
params: { id: "public-lets-chat", component: "public.launch.lets-chat", title: "Let\u2019s chat" }
|
|
295
|
-
});
|
|
296
|
-
const publicCommands = [
|
|
297
|
-
{
|
|
298
|
-
name: "landing-page",
|
|
299
|
-
description: "Open the landing page workspace tab.",
|
|
300
|
-
kind: "local",
|
|
301
|
-
handler: () => {
|
|
302
|
-
openLandingPage();
|
|
303
|
-
return { message: "Opened Landing page." };
|
|
304
|
-
}
|
|
305
|
-
},
|
|
306
|
-
{
|
|
307
|
-
name: "reach-out",
|
|
308
|
-
description: "Open the Calendly workspace tab.",
|
|
309
|
-
kind: "local",
|
|
310
|
-
handler: () => {
|
|
311
|
-
openLetsChat();
|
|
312
|
-
return { message: "Opened Let\u2019s chat." };
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
];
|
|
316
|
-
const runPublicCommand = (draft) => {
|
|
317
|
-
const command = normalizePublicCommand(draft);
|
|
318
|
-
if (command === "/landing-page") {
|
|
319
|
-
openLandingPage();
|
|
320
|
-
return true;
|
|
321
|
-
}
|
|
322
|
-
if (command === "/reach-out" || command === "/let's-chat" || command === "/lets-chat") {
|
|
323
|
-
openLetsChat();
|
|
324
|
-
return true;
|
|
325
|
-
}
|
|
326
|
-
return false;
|
|
327
|
-
};
|
|
328
|
-
useEffect2(() => {
|
|
329
|
-
const intercept = (event) => {
|
|
330
|
-
const draft = readComposerDraftFromDom();
|
|
331
|
-
if (!runPublicCommand(draft)) return false;
|
|
332
|
-
event.preventDefault();
|
|
333
|
-
event.stopPropagation();
|
|
334
|
-
return true;
|
|
335
|
-
};
|
|
336
|
-
const onClick = (event) => {
|
|
337
|
-
const target = event.target instanceof Element ? event.target : null;
|
|
338
|
-
if (!target?.closest('[data-boring-agent-part="composer-submit"], [aria-label="Submit"]')) return;
|
|
339
|
-
intercept(event);
|
|
340
|
-
};
|
|
341
|
-
const maybeAutoRunDraft = () => {
|
|
342
|
-
const draft = readComposerDraftFromDom();
|
|
343
|
-
const command = normalizePublicCommand(draft);
|
|
344
|
-
const isCommand = command === "/landing-page" || command === "/reach-out" || command === "/let's-chat" || command === "/lets-chat";
|
|
345
|
-
if (!isCommand) {
|
|
346
|
-
lastAutoRunCommandRef.current = "";
|
|
347
|
-
return;
|
|
348
|
-
}
|
|
349
|
-
if (lastAutoRunCommandRef.current === command) return;
|
|
350
|
-
lastAutoRunCommandRef.current = command;
|
|
351
|
-
runPublicCommand(draft);
|
|
352
|
-
};
|
|
353
|
-
const onKeyDown = (event) => {
|
|
354
|
-
const target = event.target instanceof Element ? event.target : null;
|
|
355
|
-
if (!target?.closest('[data-boring-agent-part="composer-input"]')) return;
|
|
356
|
-
if (event.key === "Enter" && !event.shiftKey && !event.altKey && !event.ctrlKey && !event.metaKey) intercept(event);
|
|
357
|
-
};
|
|
358
|
-
const onInput = (event) => {
|
|
359
|
-
const target = event.target instanceof Element ? event.target : null;
|
|
360
|
-
if (!target?.closest('[data-boring-agent-part="composer-input"]')) return;
|
|
361
|
-
window.setTimeout(maybeAutoRunDraft, 0);
|
|
362
|
-
};
|
|
363
|
-
document.addEventListener("click", onClick, true);
|
|
364
|
-
document.addEventListener("keydown", onKeyDown, true);
|
|
365
|
-
document.addEventListener("input", onInput, true);
|
|
366
|
-
return () => {
|
|
367
|
-
document.removeEventListener("click", onClick, true);
|
|
368
|
-
document.removeEventListener("keydown", onKeyDown, true);
|
|
369
|
-
document.removeEventListener("input", onInput, true);
|
|
370
|
-
};
|
|
371
|
-
});
|
|
372
304
|
return /* @__PURE__ */ jsxs3("div", { className: "public-chat-first-shell relative h-screen min-h-0 bg-background", children: [
|
|
373
305
|
publicShell?.showTeachingArrows !== false && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
374
306
|
/* @__PURE__ */ jsxs3("div", { className: "public-arrow public-arrow-computer", "aria-hidden": "true", children: [
|
|
@@ -411,9 +343,9 @@ function ChatFirstPublicShell({
|
|
|
411
343
|
...workspaceProps,
|
|
412
344
|
// No-auth landing should open with the workspace surface closed —
|
|
413
345
|
// a fresh load/hard refresh shows just the hero, not a re-opened
|
|
414
|
-
// panel
|
|
415
|
-
defaultSurfaceOpen: false,
|
|
416
|
-
topBarRight: /* @__PURE__ */ jsx4(
|
|
346
|
+
// panel unless the app explicitly overrides defaultSurfaceOpen.
|
|
347
|
+
defaultSurfaceOpen: workspaceProps.defaultSurfaceOpen ?? false,
|
|
348
|
+
topBarRight: /* @__PURE__ */ jsx4(PublicTopBarActions, { contact: publicShell?.contact, onSignIn: () => openAuth() }),
|
|
417
349
|
// No-auth shell has no real session yet — show just the brand, hide the
|
|
418
350
|
// "· New session" placeholder that the default TopBar would render.
|
|
419
351
|
topBarLeft: /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
@@ -433,7 +365,7 @@ function ChatFirstPublicShell({
|
|
|
433
365
|
chatParams: {
|
|
434
366
|
...workspaceProps.chatParams,
|
|
435
367
|
emptyPlacement: "hero",
|
|
436
|
-
composerPlaceholder: publicShell?.composerPlaceholder ?? "
|
|
368
|
+
composerPlaceholder: publicShell?.composerPlaceholder ?? "Sign in to continue in a private workspace",
|
|
437
369
|
hideComposerSettings: !showComposerSettings,
|
|
438
370
|
suppressPreSubmitCancelledWarning: true,
|
|
439
371
|
thinkingControl: showComposerSettings,
|
|
@@ -448,10 +380,10 @@ function ChatFirstPublicShell({
|
|
|
448
380
|
...publicShell?.emptyState
|
|
449
381
|
},
|
|
450
382
|
suggestions: publicShell?.suggestions ?? defaultPublicSuggestions,
|
|
451
|
-
commands:
|
|
383
|
+
commands: [],
|
|
452
384
|
excludeBuiltinCommands: builtinCommands.map((command) => command.name),
|
|
453
385
|
onBeforeSubmit: (draft) => {
|
|
454
|
-
|
|
386
|
+
openAuth(draft);
|
|
455
387
|
return false;
|
|
456
388
|
}
|
|
457
389
|
}
|
|
@@ -563,7 +495,7 @@ function usePendingChatDraft() {
|
|
|
563
495
|
const session = useSession();
|
|
564
496
|
const userId = session.data?.user?.id ?? null;
|
|
565
497
|
const [pending, setPending] = useState3(() => userId ? readPendingChatEntry() : null);
|
|
566
|
-
|
|
498
|
+
useEffect2(() => {
|
|
567
499
|
if (!userId) {
|
|
568
500
|
setPending(null);
|
|
569
501
|
return;
|
|
@@ -1004,7 +936,7 @@ function isPaymentRequiredNotice(notice) {
|
|
|
1004
936
|
}
|
|
1005
937
|
|
|
1006
938
|
// src/app/front/credits/useCreditBalance.ts
|
|
1007
|
-
import { useCallback, useEffect as
|
|
939
|
+
import { useCallback, useEffect as useEffect3, useRef, useState as useState4 } from "react";
|
|
1008
940
|
var CREDITS_REFRESH_EVENT = "credits:refresh";
|
|
1009
941
|
var CHECKOUT_BASELINE_STORAGE_KEY = "credits:checkout-baseline";
|
|
1010
942
|
var CHECKOUT_BASELINE_TTL_MS = 60 * 60 * 1e3;
|
|
@@ -1020,11 +952,11 @@ function useCreditBalance({
|
|
|
1020
952
|
const [lastUpdatedAt, setLastUpdatedAt] = useState4(null);
|
|
1021
953
|
const [updating, setUpdating] = useState4(false);
|
|
1022
954
|
const [error, setError] = useState4(null);
|
|
1023
|
-
const buyingRef =
|
|
1024
|
-
const burstRef =
|
|
1025
|
-
const timersRef =
|
|
1026
|
-
const burstActiveRef =
|
|
1027
|
-
const balanceRef =
|
|
955
|
+
const buyingRef = useRef(false);
|
|
956
|
+
const burstRef = useRef(0);
|
|
957
|
+
const timersRef = useRef([]);
|
|
958
|
+
const burstActiveRef = useRef(false);
|
|
959
|
+
const balanceRef = useRef(null);
|
|
1028
960
|
const refresh = useCallback(async () => {
|
|
1029
961
|
setUpdating(true);
|
|
1030
962
|
try {
|
|
@@ -1073,7 +1005,7 @@ function useCreditBalance({
|
|
|
1073
1005
|
}, delay)
|
|
1074
1006
|
);
|
|
1075
1007
|
}, [refresh]);
|
|
1076
|
-
|
|
1008
|
+
useEffect3(() => {
|
|
1077
1009
|
void refresh();
|
|
1078
1010
|
const interval = setInterval(() => void refresh(), pollMs);
|
|
1079
1011
|
const onFocus = () => void refresh();
|
|
@@ -1403,7 +1335,7 @@ function CreditsSettingsPanel({ apiBaseUrl = "", locale }) {
|
|
|
1403
1335
|
}
|
|
1404
1336
|
|
|
1405
1337
|
// src/app/front/credits/useCheckoutReturnHandler.ts
|
|
1406
|
-
import { useEffect as
|
|
1338
|
+
import { useEffect as useEffect4, useState as useState8 } from "react";
|
|
1407
1339
|
var CONFIRM_SCHEDULE_MS = [0, 1500, 3e3, 6e3, 1e4, 15e3, 22e3, 3e4, 45e3, 6e4];
|
|
1408
1340
|
async function fetchBalance(apiBaseUrl) {
|
|
1409
1341
|
try {
|
|
@@ -1437,7 +1369,7 @@ function takeStoredBaseline(now) {
|
|
|
1437
1369
|
}
|
|
1438
1370
|
function useCheckoutReturnHandler({ apiBaseUrl = "", param = "checkout" } = {}) {
|
|
1439
1371
|
const [status, setStatus] = useState8("idle");
|
|
1440
|
-
|
|
1372
|
+
useEffect4(() => {
|
|
1441
1373
|
if (typeof window === "undefined") return;
|
|
1442
1374
|
const url = new URL(window.location.href);
|
|
1443
1375
|
const marker = url.searchParams.get(param);
|