@hachej/boring-core 0.1.47 → 0.1.49

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.
@@ -1,4 +1,4 @@
1
- import { C as CoreConfig } from './types-CWtJ4kgd.js';
1
+ import { C as CoreConfig } from './types-Bb7I-83I.js';
2
2
  import { PostgresJsDatabase } from 'drizzle-orm/postgres-js';
3
3
 
4
4
  interface RunMigrationsOptions {
@@ -0,0 +1,269 @@
1
+ /*
2
+ * Default styling for the chat-first public (no-auth) shell rendered by
3
+ * `ChatFirstPublicShell`. Brand-agnostic and token-driven (`--accent`,
4
+ * `--foreground`, `--muted-foreground`) so every consumer — full-app and child
5
+ * apps alike — inherits a polished landing. Apps customize via design tokens +
6
+ * the `chatFirstPublicShell` props (copy, `models`, suggestions), not by
7
+ * re-implementing this sheet. Imported via the package's `styles.css` entry.
8
+ */
9
+
10
+ /* Subtle warm glow behind the hero so the flat background reads with depth.
11
+ Layers as a background-image over the shell's bg-background color. */
12
+ .public-chat-first-shell {
13
+ background-image: radial-gradient(
14
+ 70% 52% at 30% 22%,
15
+ color-mix(in oklch, var(--accent) 11%, transparent),
16
+ transparent 68%
17
+ );
18
+ }
19
+
20
+ /* --- Hand-drawn teaching annotations (public no-auth shell) --- */
21
+ .public-arrow {
22
+ pointer-events: none;
23
+ position: fixed;
24
+ z-index: 20;
25
+ display: flex;
26
+ color: color-mix(in oklch, var(--foreground) 55%, transparent);
27
+ }
28
+
29
+ .public-arrow-svg {
30
+ overflow: visible;
31
+ }
32
+
33
+ .public-arrow .paw-stroke {
34
+ fill: none;
35
+ stroke: currentColor;
36
+ stroke-width: 3.5;
37
+ stroke-linecap: round;
38
+ stroke-linejoin: round;
39
+ }
40
+
41
+ .public-arrow-label {
42
+ font-family: "Caveat", ui-rounded, "Segoe Print", cursive;
43
+ font-weight: 700;
44
+ font-size: 26px;
45
+ line-height: 1;
46
+ color: color-mix(in oklch, var(--foreground) 78%, transparent);
47
+ white-space: nowrap;
48
+ }
49
+
50
+ /* Right: points to the workspace surface button on the right edge. */
51
+ .public-arrow-computer {
52
+ right: 12px;
53
+ bottom: 374px;
54
+ align-items: flex-end;
55
+ gap: 4px;
56
+ }
57
+
58
+ .public-arrow-computer .public-arrow-label {
59
+ margin-bottom: 6px;
60
+ transform: rotate(-5deg);
61
+ }
62
+
63
+ .public-arrow-computer .public-arrow-svg {
64
+ width: 178px;
65
+ height: 134px;
66
+ }
67
+
68
+ /* Bottom: points up to the composer (the agent input). */
69
+ .public-arrow-agent {
70
+ left: 50%;
71
+ bottom: 78px;
72
+ flex-direction: column;
73
+ align-items: center;
74
+ transform: translateX(-50%);
75
+ gap: 2px;
76
+ }
77
+
78
+ .public-arrow-agent .public-arrow-svg {
79
+ width: 96px;
80
+ height: 92px;
81
+ }
82
+
83
+ .public-arrow-agent .public-arrow-label {
84
+ transform: rotate(-4deg);
85
+ }
86
+
87
+ /* Teaching arrows + bottom-left sign-in card are edge decorations that need
88
+ horizontal room — keep them on normal screens, hide only when genuinely
89
+ narrow (where they'd collide with the centered hero/composer). */
90
+ @media (max-width: 1100px) {
91
+ .public-arrow,
92
+ .public-chat-first-shell > aside {
93
+ display: none;
94
+ }
95
+ }
96
+
97
+ /* The composer's model/thinking picker menu expands upward over the hero. It
98
+ lives deep in the chat content (a non-positioned ancestor), so a z-index
99
+ can't lift it above the fixed teaching arrow / sign-in card. Instead, while a
100
+ picker menu is open, hide the bottom "Ask your AI here" arrow and the sign-in
101
+ card so the menu reads cleanly; they return as soon as it closes. */
102
+ .public-chat-first-shell:has([data-boring-agent-part="model-picker-menu"], [data-boring-agent-part="thinking-picker-menu"]) .public-arrow-agent,
103
+ .public-chat-first-shell:has([data-boring-agent-part="model-picker-menu"], [data-boring-agent-part="thinking-picker-menu"]) > aside {
104
+ display: none;
105
+ }
106
+
107
+ /* Hide the chat-pane header strip ("New session") in the no-auth shell — there's
108
+ only ever a single pane here, so its title/drag/close bar is pure noise. */
109
+ .public-chat-first-shell .dv-chat-stage .dv-tabs-and-actions-container {
110
+ display: none;
111
+ }
112
+
113
+ /* Keep opened workspace panes above the teaching arrows. */
114
+ .public-chat-first-shell [data-boring-workspace-part="workbench"] {
115
+ z-index: 30;
116
+ }
117
+
118
+ /* --- Prominent hero (public no-auth empty state only) --- */
119
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] {
120
+ max-width: min(1120px, 92vw);
121
+ }
122
+
123
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] > div:first-child {
124
+ font-size: 12px;
125
+ letter-spacing: 0.18em;
126
+ }
127
+
128
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] h3 {
129
+ margin-top: 22px;
130
+ font-size: clamp(30px, 4.4vw, 60px);
131
+ font-weight: 600;
132
+ line-height: 1;
133
+ letter-spacing: -0.04em;
134
+ white-space: nowrap;
135
+ }
136
+
137
+ /* Editorial accent on the closing period (the title string omits its own). */
138
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] h3::after {
139
+ content: ".";
140
+ color: var(--accent);
141
+ }
142
+
143
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] > p {
144
+ display: block;
145
+ /* Let the subhead breathe: more space from the title and looser leading
146
+ between the two lines. */
147
+ margin-top: 18px;
148
+ max-width: 58ch;
149
+ font-size: 17px;
150
+ line-height: 1.85;
151
+ /* Honor the explicit \n after "Choose the AI you trust." in the subhead. */
152
+ white-space: pre-line;
153
+ }
154
+
155
+ @media (max-width: 560px) {
156
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] h3 {
157
+ white-space: normal;
158
+ }
159
+ }
160
+
161
+ /* Top-bias the hero + composer group instead of vertically centering it, so it
162
+ doesn't float to mid-screen (with a large empty band on top) on tall windows. */
163
+ .public-chat-first-shell [class*="justify-center"]:has([data-boring-agent-part="empty-state"]) {
164
+ justify-content: flex-start;
165
+ padding-top: clamp(28px, 9vh, 132px);
166
+ }
167
+
168
+ /* Move the /command cards to the bottom of the hero block (after the footer),
169
+ so they sit directly above the composer. The empty-state is a flex column,
170
+ so `order` reorders without touching the React tree. */
171
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] .public-hero-foot {
172
+ order: 10;
173
+ }
174
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] [data-boring-agent-part="suggestion-grid"] {
175
+ order: 20;
176
+ }
177
+
178
+ /* Command launcher cards — make the slash commands read as runnable. */
179
+ .public-chat-first-shell [data-boring-agent-part="suggestion-grid"] {
180
+ margin-top: 28px;
181
+ max-width: 560px;
182
+ margin-left: auto;
183
+ margin-right: auto;
184
+ /* Match the composer's rounding + 1px outline so cards + input read as one
185
+ design system (composer rail is rounded-[28px] with a border/0.7 outline). */
186
+ border-radius: 20px;
187
+ }
188
+
189
+ /* Accent send button — make the composer's primary action carry the accent so
190
+ the orange reads as intentional, not just decoration on the slash cards.
191
+ Scoped to the public hero; the shared composer elsewhere stays neutral. */
192
+ .public-chat-first-shell [data-boring-agent-part="composer-submit"] {
193
+ background-color: var(--accent);
194
+ }
195
+
196
+ .public-chat-first-shell [data-boring-agent-part="composer-submit"]:hover {
197
+ background-color: color-mix(in oklch, var(--accent) 90%, var(--foreground));
198
+ }
199
+
200
+ .public-chat-first-shell [data-boring-agent-part="suggestion-card"] {
201
+ padding-top: 15px;
202
+ padding-bottom: 15px;
203
+ }
204
+
205
+ .public-chat-first-shell [data-boring-agent-part="suggestion-card"] > span > span:first-child {
206
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, monospace);
207
+ font-size: 13.5px;
208
+ font-weight: 600;
209
+ letter-spacing: -0.01em;
210
+ color: var(--accent);
211
+ }
212
+
213
+ .public-chat-first-shell [data-boring-agent-part="suggestion-card"] > span > span:nth-child(2) {
214
+ margin-top: 2px;
215
+ font-size: 12.5px;
216
+ }
217
+
218
+ /* Single compact footer line: "providers … · Open source [logo]". */
219
+ .public-chat-first-shell .public-hero-foot {
220
+ display: flex;
221
+ flex-direction: row;
222
+ flex-wrap: wrap;
223
+ align-items: center;
224
+ justify-content: center;
225
+ gap: 8px;
226
+ margin-top: 30px;
227
+ font-size: 12.5px;
228
+ line-height: 1.4;
229
+ }
230
+
231
+ .public-chat-first-shell .public-hero-foot span {
232
+ color: var(--muted-foreground);
233
+ }
234
+
235
+ .public-chat-first-shell .public-hero-foot .public-hero-providers {
236
+ color: color-mix(in oklch, var(--foreground) 60%, transparent);
237
+ letter-spacing: 0.01em;
238
+ }
239
+
240
+ .public-chat-first-shell .public-hero-foot .public-hero-dot {
241
+ color: var(--muted-foreground);
242
+ opacity: 0.5;
243
+ }
244
+
245
+ .public-chat-first-shell .public-hero-foot .public-hero-github {
246
+ display: inline-flex;
247
+ align-items: center;
248
+ gap: 5px;
249
+ color: var(--foreground);
250
+ font-weight: 500;
251
+ text-decoration: none;
252
+ transition: color 0.15s ease;
253
+ }
254
+
255
+ .public-chat-first-shell .public-hero-foot .public-hero-github svg {
256
+ width: 15px;
257
+ height: 15px;
258
+ }
259
+
260
+ .public-chat-first-shell .public-hero-foot .public-hero-github:hover {
261
+ color: var(--accent);
262
+ }
263
+
264
+ /* Emphasize the trusted choice in the subhead without it reading as a link. */
265
+ .public-chat-first-shell [data-boring-agent-part="empty-state"] > p .public-hero-trust {
266
+ font-style: normal;
267
+ font-weight: 600;
268
+ color: color-mix(in oklch, var(--foreground) 88%, transparent);
269
+ }
@@ -3,14 +3,18 @@ import {
3
3
  UserMenu,
4
4
  WorkspaceSwitcher,
5
5
  routes,
6
+ useConfig,
6
7
  useCurrentWorkspace,
7
8
  useSession,
8
9
  useSignIn,
9
10
  useSignUp,
10
11
  useWorkspaceRouteStatus
11
- } from "../../chunk-VYXEXOCO.js";
12
+ } from "../../chunk-P4RF2D7H.js";
12
13
  import "../../chunk-HYNKZSTF.js";
13
- import "../../chunk-LIBHVT7V.js";
14
+ import {
15
+ isRuntimeEmailVerificationEnabled
16
+ } from "../../chunk-I4PGL4ZD.js";
17
+ import "../../chunk-QZGYKLXB.js";
14
18
  import "../../chunk-MLKGABMK.js";
15
19
 
16
20
  // src/app/front/CoreWorkspaceAgentFront.tsx
@@ -108,6 +112,7 @@ function AuthCard({
108
112
  onClose
109
113
  }) {
110
114
  const navigate = useNavigate();
115
+ const config = useConfig();
111
116
  const signIn = useSignIn();
112
117
  const signUp = useSignUp();
113
118
  const [mode, setMode] = useState("signin");
@@ -127,6 +132,10 @@ function AuthCard({
127
132
  return;
128
133
  }
129
134
  onClose?.();
135
+ if (mode === "signup" && isRuntimeEmailVerificationEnabled(config)) {
136
+ navigate(routes.verifyEmail, { replace: true });
137
+ return;
138
+ }
130
139
  navigate(returnTo, { replace: true });
131
140
  } catch (err) {
132
141
  setError(err instanceof Error ? err.message : `${mode === "signin" ? "Sign in" : "Sign up"} failed`);
@@ -521,10 +530,29 @@ function HomeRedirect({
521
530
  workspaceHref,
522
531
  chatEntryMode,
523
532
  appTitle,
533
+ topBarLeft,
534
+ topBarRight,
524
535
  workspaceProps,
525
536
  chatFirstPublicShell,
526
537
  chatFirstPublicWorkspaceProps
527
538
  }) {
539
+ const config = useConfig();
540
+ const resolvedAppTitle = appTitle ?? config.appName;
541
+ const resolvedTopBarLeft = topBarLeft === void 0 ? /* @__PURE__ */ jsx5(WorkspaceSwitcher, { appTitle: resolvedAppTitle }) : topBarLeft;
542
+ const resolvedLoadingFallback = loadingFallback ?? /* @__PURE__ */ jsx5(
543
+ WorkspaceLoadingPage,
544
+ {
545
+ appTitle: resolvedAppTitle,
546
+ topBarLeft: resolvedTopBarLeft,
547
+ topBarRight
548
+ }
549
+ );
550
+ const resolvedWorkspaceProps = {
551
+ ...workspaceProps,
552
+ appTitle: resolvedAppTitle,
553
+ topBarLeft: resolvedTopBarLeft,
554
+ topBarRight
555
+ };
528
556
  const location = useLocation2();
529
557
  const session = useSession();
530
558
  const workspace = useCurrentWorkspace();
@@ -539,9 +567,9 @@ function HomeRedirect({
539
567
  return /* @__PURE__ */ jsx5(
540
568
  ChatFirstPublicShell,
541
569
  {
542
- appTitle,
570
+ appTitle: resolvedAppTitle,
543
571
  publicShell: chatFirstPublicShell,
544
- workspaceProps: mergePublicWorkspaceProps(workspaceProps, chatFirstPublicWorkspaceProps)
572
+ workspaceProps: mergePublicWorkspaceProps(resolvedWorkspaceProps, chatFirstPublicWorkspaceProps)
545
573
  }
546
574
  );
547
575
  }
@@ -549,14 +577,14 @@ function HomeRedirect({
549
577
  return /* @__PURE__ */ jsx5(
550
578
  ChatFirstAuthenticatedShell,
551
579
  {
552
- appTitle,
580
+ appTitle: resolvedAppTitle,
553
581
  workspaceId: pendingChatEntry?.intendedWorkspaceId ?? DEFAULT_CHAT_FIRST_PENDING_WORKSPACE_ID,
554
582
  initialDraft: pendingChatEntry?.draft,
555
- workspaceProps
583
+ workspaceProps: resolvedWorkspaceProps
556
584
  }
557
585
  );
558
586
  }
559
- if (!workspace) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingFallback });
587
+ if (!workspace) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
560
588
  return /* @__PURE__ */ jsx5(Navigate, { to: workspaceHref(workspace.id), replace: true });
561
589
  }
562
590
  function WorkspaceRouteErrorPage({ status, message }) {
@@ -573,10 +601,29 @@ function WorkspaceRoute({
573
601
  workspaceProps,
574
602
  chatEntryMode,
575
603
  appTitle,
604
+ topBarLeft,
605
+ topBarRight,
576
606
  workspaceRoute,
577
607
  chatFirstPublicShell,
578
608
  chatFirstPublicWorkspaceProps
579
609
  }) {
610
+ const config = useConfig();
611
+ const resolvedAppTitle = appTitle ?? config.appName;
612
+ const resolvedTopBarLeft = topBarLeft === void 0 ? /* @__PURE__ */ jsx5(WorkspaceSwitcher, { appTitle: resolvedAppTitle }) : topBarLeft;
613
+ const resolvedLoadingFallback = loadingFallback ?? /* @__PURE__ */ jsx5(
614
+ WorkspaceLoadingPage,
615
+ {
616
+ appTitle: resolvedAppTitle,
617
+ topBarLeft: resolvedTopBarLeft,
618
+ topBarRight
619
+ }
620
+ );
621
+ const resolvedWorkspaceProps = {
622
+ ...workspaceProps,
623
+ appTitle: resolvedAppTitle,
624
+ topBarLeft: resolvedTopBarLeft,
625
+ topBarRight
626
+ };
580
627
  const params = useParams();
581
628
  const location = useLocation2();
582
629
  const session = useSession();
@@ -592,22 +639,22 @@ function WorkspaceRoute({
592
639
  location.hash
593
640
  ) || pendingChatEntry?.returnTo === "/" && currentWorkspace?.id === workspaceId);
594
641
  const requestHeaders = useMemo(
595
- () => ({ ...workspaceProps.requestHeaders, "x-boring-workspace-id": workspaceId }),
596
- [workspaceId, workspaceProps.requestHeaders]
642
+ () => ({ ...resolvedWorkspaceProps.requestHeaders, "x-boring-workspace-id": workspaceId }),
643
+ [workspaceId, resolvedWorkspaceProps.requestHeaders]
597
644
  );
598
645
  const authHeaders = useMemo(
599
- () => ({ ...workspaceProps.authHeaders, "x-boring-workspace-id": workspaceId }),
600
- [workspaceId, workspaceProps.authHeaders]
646
+ () => ({ ...resolvedWorkspaceProps.authHeaders, "x-boring-workspace-id": workspaceId }),
647
+ [workspaceId, resolvedWorkspaceProps.authHeaders]
601
648
  );
602
- if (!workspaceId) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingFallback });
649
+ if (!workspaceId) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
603
650
  if (!session.data?.user && chatEntryMode === "chat-first") {
604
651
  return /* @__PURE__ */ jsx5(
605
652
  ChatFirstPublicShell,
606
653
  {
607
- appTitle,
654
+ appTitle: resolvedAppTitle,
608
655
  intendedWorkspaceId: workspaceId,
609
656
  publicShell: chatFirstPublicShell,
610
- workspaceProps: mergePublicWorkspaceProps(workspaceProps, chatFirstPublicWorkspaceProps)
657
+ workspaceProps: mergePublicWorkspaceProps(resolvedWorkspaceProps, chatFirstPublicWorkspaceProps)
611
658
  }
612
659
  );
613
660
  }
@@ -618,21 +665,21 @@ function WorkspaceRoute({
618
665
  return /* @__PURE__ */ jsx5(
619
666
  ChatFirstAuthenticatedShell,
620
667
  {
621
- appTitle,
668
+ appTitle: resolvedAppTitle,
622
669
  workspaceId,
623
670
  initialDraft: pendingChatEntry?.draft,
624
- workspaceProps
671
+ workspaceProps: resolvedWorkspaceProps
625
672
  }
626
673
  );
627
674
  }
628
- if (routeStatus.status !== "matched" || currentWorkspace?.id !== workspaceId) return /* @__PURE__ */ jsx5(Fragment3, { children: loadingFallback });
675
+ if (routeStatus.status !== "matched" || currentWorkspace?.id !== workspaceId) return /* @__PURE__ */ jsx5(Fragment3, { children: resolvedLoadingFallback });
629
676
  const shouldRestorePendingDraft = restorePendingDraft && Boolean(pendingChatEntry?.draft);
630
677
  const chatParams = {
631
- ...workspaceProps.chatParams,
678
+ ...resolvedWorkspaceProps.chatParams,
632
679
  ...shouldRestorePendingDraft ? { initialDraft: pendingChatEntry?.draft } : {},
633
680
  ...shouldRestorePendingDraft ? { autoSubmitInitialDraft: true } : {},
634
681
  onBeforeSubmit: async (draft, ctx) => {
635
- const existing = workspaceProps.chatParams?.onBeforeSubmit;
682
+ const existing = resolvedWorkspaceProps.chatParams?.onBeforeSubmit;
636
683
  const result = await existing?.(draft, ctx);
637
684
  if (result !== false) clearPendingChatEntry();
638
685
  return result;
@@ -641,9 +688,9 @@ function WorkspaceRoute({
641
688
  return /* @__PURE__ */ jsx5(
642
689
  WorkspaceAgentFront2,
643
690
  {
644
- ...workspaceProps,
691
+ ...resolvedWorkspaceProps,
645
692
  workspaceId,
646
- workspaceLabel: workspaceProps.workspaceLabel ?? currentWorkspace.name,
693
+ workspaceLabel: resolvedWorkspaceProps.workspaceLabel ?? currentWorkspace.name,
647
694
  requestHeaders,
648
695
  authHeaders,
649
696
  chatParams,
@@ -667,9 +714,9 @@ function CoreWorkspaceAgentFront({
667
714
  workspaceHref = (workspaceId) => `/workspace/${workspaceId}`,
668
715
  loadingFallback,
669
716
  bootPreloadPaths,
670
- topBarLeft = /* @__PURE__ */ jsx5(WorkspaceSwitcher, {}),
717
+ topBarLeft,
671
718
  topBarRight = /* @__PURE__ */ jsx5(DefaultTopBarRight, {}),
672
- appTitle = "Sovereign Workspace",
719
+ appTitle,
673
720
  bridgeEndpoint = "/api/v1/ui",
674
721
  hotReload = false,
675
722
  chatEntryMode = "auth-first",
@@ -683,19 +730,8 @@ function CoreWorkspaceAgentFront({
683
730
  "CoreWorkspaceAgentFront does not support hotReload yet; use static plugin consumption or WorkspaceAgentFront for standalone hot reload."
684
731
  );
685
732
  }
686
- const resolvedLoadingFallback = loadingFallback ?? /* @__PURE__ */ jsx5(
687
- WorkspaceLoadingPage,
688
- {
689
- appTitle,
690
- topBarLeft,
691
- topBarRight
692
- }
693
- );
694
- const resolvedWorkspaceProps = {
733
+ const routedWorkspaceProps = {
695
734
  ...workspaceProps,
696
- appTitle,
697
- topBarLeft,
698
- topBarRight,
699
735
  bridgeEndpoint
700
736
  };
701
737
  return /* @__PURE__ */ jsxs4(
@@ -714,11 +750,13 @@ function CoreWorkspaceAgentFront({
714
750
  element: /* @__PURE__ */ jsx5(
715
751
  HomeRedirect,
716
752
  {
717
- loadingFallback: resolvedLoadingFallback,
753
+ loadingFallback,
718
754
  workspaceHref,
719
755
  chatEntryMode,
720
756
  appTitle,
721
- workspaceProps: resolvedWorkspaceProps,
757
+ topBarLeft,
758
+ topBarRight,
759
+ workspaceProps: routedWorkspaceProps,
722
760
  chatFirstPublicShell,
723
761
  chatFirstPublicWorkspaceProps
724
762
  }
@@ -733,11 +771,13 @@ function CoreWorkspaceAgentFront({
733
771
  WorkspaceRoute,
734
772
  {
735
773
  workspaceIdParam,
736
- loadingFallback: resolvedLoadingFallback,
774
+ loadingFallback,
737
775
  bootPreloadPaths,
738
- workspaceProps: resolvedWorkspaceProps,
776
+ workspaceProps: routedWorkspaceProps,
739
777
  chatEntryMode,
740
778
  appTitle,
779
+ topBarLeft,
780
+ topBarRight,
741
781
  workspaceRoute,
742
782
  chatFirstPublicShell,
743
783
  chatFirstPublicWorkspaceProps
@@ -1351,7 +1391,7 @@ function BuyCreditsNoticeAction({ apiBaseUrl = "", label = "Buy credits" }) {
1351
1391
  size: "sm",
1352
1392
  onClick: () => void onBuy(),
1353
1393
  disabled: buying,
1354
- className: "bg-foreground text-background hover:bg-foreground/90",
1394
+ className: "boring-buy-credits-notice-button",
1355
1395
  children: buying ? "Opening\u2026" : label
1356
1396
  }
1357
1397
  ),
@@ -4,3 +4,50 @@
4
4
  @import "../../front/theme.css";
5
5
  @import "@hachej/boring-workspace/globals.css";
6
6
  @import "@hachej/boring-agent/front/styles.css";
7
+
8
+ /* Default styling for the chat-first public (no-auth) shell. Imported last so
9
+ its plain rules sit after Tailwind's utilities in the cascade. */
10
+ @import "./chatFirst/chatFirstPublicShell.css";
11
+
12
+ /* Credit notice CTA renders inside the agent subtree. Keep this selector after
13
+ Tailwind + agent globals so it beats both the Button variant colors and
14
+ [data-boring-agent] button color reset. */
15
+ [data-boring-agent] .boring-buy-credits-notice-button,
16
+ .boring-buy-credits-notice-button {
17
+ border: 1px solid color-mix(in oklab, var(--foreground) 18%, transparent);
18
+ background: var(--foreground);
19
+ color: var(--background);
20
+ }
21
+
22
+ [data-boring-agent] .boring-buy-credits-notice-button:hover,
23
+ .boring-buy-credits-notice-button:hover {
24
+ background: color-mix(in oklab, var(--foreground) 90%, transparent);
25
+ }
26
+
27
+ html[data-theme="dark"] [data-boring-agent] .boring-buy-credits-notice-button,
28
+ html[data-theme="dark"] .boring-buy-credits-notice-button {
29
+ border-color: oklch(1 0 0 / 0.2);
30
+ background: oklch(0.985 0 0);
31
+ color: oklch(0.145 0.004 285.823);
32
+ }
33
+
34
+ html[data-theme="dark"] [data-boring-agent] .boring-buy-credits-notice-button:hover,
35
+ html[data-theme="dark"] .boring-buy-credits-notice-button:hover {
36
+ background: oklch(0.9 0 0);
37
+ }
38
+
39
+ /* The public-shell chat composer submit button sits on a brand/accent foreground.
40
+ The generic inverse text is too low-contrast on that amber CTA, so pin the public
41
+ shell icon to dark ink while leaving authenticated workspace composers alone. */
42
+ .public-chat-first-shell [data-boring-agent] [data-boring-agent-part="composer-submit"] {
43
+ color: oklch(0.145 0.004 285.823) !important;
44
+ }
45
+
46
+ html[data-theme="dark"] .public-chat-first-shell [data-boring-agent] [data-boring-agent-part="composer-submit"] {
47
+ background: oklch(0.985 0 0) !important;
48
+ color: oklch(0.145 0.004 285.823) !important;
49
+ }
50
+
51
+ html[data-theme="dark"] .public-chat-first-shell [data-boring-agent] [data-boring-agent-part="composer-submit"]:hover {
52
+ background: oklch(0.9 0 0) !important;
53
+ }
@@ -2,10 +2,10 @@ import { RuntimeProvisioningContribution, RegisterAgentRoutesOptions } from '@ha
2
2
  import { DirPluginEntry, CreateWorkspaceAgentServerOptions } from '@hachej/boring-workspace/app/server';
3
3
  import { WorkspaceServerPlugin } from '@hachej/boring-workspace/server';
4
4
  import { FastifyInstance } from 'fastify';
5
- import { C as CoreConfig } from '../../types-CWtJ4kgd.js';
6
- import { TelemetrySink } from '../../shared/index.js';
7
- import { B as BetterAuthInstance, L as LoadConfigOptions } from '../../authHook-DtzhSmqS.js';
8
- import { D as Database, U as UserStore, W as WorkspaceStore } from '../../connection-C5SiqoNc.js';
5
+ import { C as CoreConfig } from '../../types-Bb7I-83I.js';
6
+ import { T as TelemetrySink } from '../../telemetry-DR18MeI0.js';
7
+ import { B as BetterAuthInstance, L as LoadConfigOptions } from '../../authHook-BbgCBHjP.js';
8
+ import { D as Database, U as UserStore, W as WorkspaceStore } from '../../connection-B1iC6B-w.js';
9
9
  import { IncomingMessage, ServerResponse } from 'node:http';
10
10
  import 'better-auth';
11
11
  import 'drizzle-orm/postgres-js';
@@ -9,20 +9,21 @@ import {
9
9
  registerRoutes,
10
10
  registerSettingsRoutes,
11
11
  registerWorkspaceRoutes
12
- } from "../../chunk-6GAQRQKO.js";
12
+ } from "../../chunk-ZMS6O4CY.js";
13
13
  import {
14
14
  PostgresUserStore,
15
15
  PostgresWorkspaceStore,
16
16
  createDatabase,
17
17
  telemetryEvents
18
- } from "../../chunk-FZC3VL5D.js";
18
+ } from "../../chunk-BVZ2YT3M.js";
19
19
  import {
20
20
  noopTelemetry,
21
21
  safeCapture
22
22
  } from "../../chunk-AQBXNPMD.js";
23
+ import "../../chunk-I4PGL4ZD.js";
23
24
  import {
24
25
  ERROR_CODES
25
- } from "../../chunk-LIBHVT7V.js";
26
+ } from "../../chunk-QZGYKLXB.js";
26
27
  import "../../chunk-MLKGABMK.js";
27
28
 
28
29
  // src/app/server/createCoreWorkspaceAgentServer.ts
@@ -1,8 +1,8 @@
1
- import { C as CoreConfig, R as RuntimeConfig } from './types-CWtJ4kgd.js';
1
+ import { C as CoreConfig, R as RuntimeConfig } from './types-Bb7I-83I.js';
2
2
  import { FastifyPluginAsync } from 'fastify';
3
3
  import { Auth } from 'better-auth';
4
- import { W as WorkspaceStore, D as Database } from './connection-C5SiqoNc.js';
5
- import { TelemetrySink } from './shared/index.js';
4
+ import { W as WorkspaceStore, D as Database } from './connection-B1iC6B-w.js';
5
+ import { T as TelemetrySink } from './telemetry-DR18MeI0.js';
6
6
 
7
7
  interface LoadConfigOptions {
8
8
  tomlPath?: string;
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ERROR_CODES,
3
3
  HttpError
4
- } from "./chunk-LIBHVT7V.js";
4
+ } from "./chunk-QZGYKLXB.js";
5
5
  import {
6
6
  __export
7
7
  } from "./chunk-MLKGABMK.js";
@@ -0,0 +1,17 @@
1
+ // src/shared/authPolicy.ts
2
+ function isCoreEmailVerificationEnabled(config) {
3
+ return Boolean(config.auth.mail);
4
+ }
5
+ function isRuntimeEmailVerificationEnabled(config) {
6
+ return config?.features.emailVerification === true;
7
+ }
8
+ function canUseProtectedApi(user, requireEmailVerification) {
9
+ if (!user) return false;
10
+ return !requireEmailVerification || user.emailVerified === true;
11
+ }
12
+
13
+ export {
14
+ isCoreEmailVerificationEnabled,
15
+ isRuntimeEmailVerificationEnabled,
16
+ canUseProtectedApi
17
+ };