stagent 0.3.5 → 0.4.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 +11 -0
- package/dist/cli.js +39 -10
- package/drizzle.config.ts +3 -1
- package/package.json +3 -1
- package/src/app/api/book/bookmarks/route.ts +73 -0
- package/src/app/api/book/progress/route.ts +79 -0
- package/src/app/api/book/regenerate/route.ts +111 -0
- package/src/app/api/book/stage/route.ts +13 -0
- package/src/app/api/chat/conversations/[id]/respond/route.ts +19 -20
- package/src/app/api/chat/conversations/[id]/route.ts +2 -1
- package/src/app/api/documents/[id]/route.ts +34 -2
- package/src/app/api/documents/route.ts +91 -0
- package/src/app/api/settings/runtime/route.ts +46 -0
- package/src/app/book/page.tsx +14 -0
- package/src/app/chat/page.tsx +7 -1
- package/src/app/globals.css +375 -0
- package/src/app/projects/[id]/page.tsx +31 -6
- package/src/app/settings/page.tsx +2 -0
- package/src/app/{playbook → user-guide}/[slug]/page.tsx +12 -2
- package/src/app/{playbook → user-guide}/page.tsx +2 -2
- package/src/app/workflows/[id]/page.tsx +28 -2
- package/src/components/book/book-reader.tsx +801 -0
- package/src/components/book/chapter-generation-bar.tsx +109 -0
- package/src/components/book/content-blocks.tsx +432 -0
- package/src/components/book/path-progress.tsx +33 -0
- package/src/components/book/path-selector.tsx +42 -0
- package/src/components/book/try-it-now.tsx +164 -0
- package/src/components/chat/chat-activity-indicator.tsx +92 -0
- package/src/components/chat/chat-message-list.tsx +3 -0
- package/src/components/chat/chat-message.tsx +22 -6
- package/src/components/chat/chat-permission-request.tsx +5 -1
- package/src/components/chat/chat-question.tsx +3 -0
- package/src/components/chat/chat-shell.tsx +130 -19
- package/src/components/chat/conversation-list.tsx +8 -2
- package/src/components/playbook/adoption-heatmap.tsx +1 -1
- package/src/components/playbook/journey-card.tsx +1 -1
- package/src/components/playbook/playbook-card.tsx +1 -1
- package/src/components/playbook/playbook-detail-view.tsx +15 -5
- package/src/components/playbook/playbook-homepage.tsx +1 -1
- package/src/components/playbook/playbook-updated-badge.tsx +1 -1
- package/src/components/projects/project-detail.tsx +147 -27
- package/src/components/projects/project-form-sheet.tsx +6 -2
- package/src/components/projects/project-list.tsx +1 -1
- package/src/components/settings/runtime-timeout-section.tsx +170 -0
- package/src/components/shared/app-sidebar.tsx +7 -1
- package/src/components/shared/command-palette.tsx +4 -4
- package/src/hooks/use-chapter-generation.ts +255 -0
- package/src/lib/agents/claude-agent.ts +12 -6
- package/src/lib/agents/runtime/claude.ts +29 -3
- package/src/lib/book/chapter-generator.ts +193 -0
- package/src/lib/book/chapter-mapping.ts +91 -0
- package/src/lib/book/content.ts +251 -0
- package/src/lib/book/markdown-parser.ts +317 -0
- package/src/lib/book/reading-paths.ts +82 -0
- package/src/lib/book/types.ts +152 -0
- package/src/lib/book/update-detector.ts +157 -0
- package/src/lib/chat/codex-engine.ts +537 -0
- package/src/lib/chat/context-builder.ts +18 -4
- package/src/lib/chat/engine.ts +116 -39
- package/src/lib/chat/model-discovery.ts +13 -5
- package/src/lib/chat/permission-bridge.ts +14 -2
- package/src/lib/chat/stagent-tools.ts +2 -0
- package/src/lib/chat/system-prompt.ts +16 -1
- package/src/lib/chat/tools/chat-history-tools.ts +177 -0
- package/src/lib/chat/tools/document-tools.ts +204 -0
- package/src/lib/chat/tools/settings-tools.ts +30 -3
- package/src/lib/chat/types.ts +8 -1
- package/src/lib/constants/settings.ts +2 -0
- package/src/lib/data/chat.ts +83 -2
- package/src/lib/data/clear.ts +8 -0
- package/src/lib/db/bootstrap.ts +24 -0
- package/src/lib/db/schema.ts +32 -0
- package/src/lib/docs/types.ts +9 -0
- /package/src/app/api/{playbook → user-guide}/status/route.ts +0 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { NextRequest, NextResponse } from "next/server";
|
|
2
|
+
import { getSetting, setSetting } from "@/lib/settings/helpers";
|
|
3
|
+
import { SETTINGS_KEYS } from "@/lib/constants/settings";
|
|
4
|
+
|
|
5
|
+
export async function GET() {
|
|
6
|
+
const sdkTimeoutSeconds = await getSetting(SETTINGS_KEYS.SDK_TIMEOUT_SECONDS);
|
|
7
|
+
const maxTurns = await getSetting(SETTINGS_KEYS.MAX_TURNS);
|
|
8
|
+
return NextResponse.json({
|
|
9
|
+
sdkTimeoutSeconds: sdkTimeoutSeconds ?? "60",
|
|
10
|
+
maxTurns: maxTurns ?? "10",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function POST(req: NextRequest) {
|
|
15
|
+
const body = await req.json();
|
|
16
|
+
|
|
17
|
+
if (body.sdkTimeoutSeconds !== undefined) {
|
|
18
|
+
const seconds = parseInt(body.sdkTimeoutSeconds, 10);
|
|
19
|
+
if (isNaN(seconds) || seconds < 10 || seconds > 300) {
|
|
20
|
+
return NextResponse.json(
|
|
21
|
+
{ error: "sdkTimeoutSeconds must be between 10 and 300" },
|
|
22
|
+
{ status: 400 }
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
await setSetting(SETTINGS_KEYS.SDK_TIMEOUT_SECONDS, String(seconds));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (body.maxTurns !== undefined) {
|
|
29
|
+
const turns = parseInt(body.maxTurns, 10);
|
|
30
|
+
if (isNaN(turns) || turns < 1 || turns > 50) {
|
|
31
|
+
return NextResponse.json(
|
|
32
|
+
{ error: "maxTurns must be between 1 and 50" },
|
|
33
|
+
{ status: 400 }
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
await setSetting(SETTINGS_KEYS.MAX_TURNS, String(turns));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const sdkTimeoutSeconds = await getSetting(SETTINGS_KEYS.SDK_TIMEOUT_SECONDS);
|
|
40
|
+
const maxTurns = await getSetting(SETTINGS_KEYS.MAX_TURNS);
|
|
41
|
+
|
|
42
|
+
return NextResponse.json({
|
|
43
|
+
sdkTimeoutSeconds: sdkTimeoutSeconds ?? "60",
|
|
44
|
+
maxTurns: maxTurns ?? "10",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { PageShell } from "@/components/shared/page-shell";
|
|
2
|
+
import { BookReader } from "@/components/book/book-reader";
|
|
3
|
+
import { getBook } from "@/lib/book/content";
|
|
4
|
+
|
|
5
|
+
export const dynamic = "force-dynamic";
|
|
6
|
+
|
|
7
|
+
export default function BookPage() {
|
|
8
|
+
const book = getBook();
|
|
9
|
+
return (
|
|
10
|
+
<PageShell fullBleed>
|
|
11
|
+
<BookReader chapters={book.chapters} />
|
|
12
|
+
</PageShell>
|
|
13
|
+
);
|
|
14
|
+
}
|
package/src/app/chat/page.tsx
CHANGED
|
@@ -4,7 +4,12 @@ import { ChatShell } from "@/components/chat/chat-shell";
|
|
|
4
4
|
|
|
5
5
|
export const dynamic = "force-dynamic";
|
|
6
6
|
|
|
7
|
-
export default async function ChatPage(
|
|
7
|
+
export default async function ChatPage({
|
|
8
|
+
searchParams,
|
|
9
|
+
}: {
|
|
10
|
+
searchParams: Promise<{ c?: string }>;
|
|
11
|
+
}) {
|
|
12
|
+
const params = await searchParams;
|
|
8
13
|
const [conversations, promptCategories] = await Promise.all([
|
|
9
14
|
listConversations({ status: "active" }),
|
|
10
15
|
getPromptCategories(),
|
|
@@ -14,6 +19,7 @@ export default async function ChatPage() {
|
|
|
14
19
|
<ChatShell
|
|
15
20
|
initialConversations={conversations}
|
|
16
21
|
promptCategories={promptCategories}
|
|
22
|
+
initialActiveId={params.c ?? null}
|
|
17
23
|
/>
|
|
18
24
|
);
|
|
19
25
|
}
|
package/src/app/globals.css
CHANGED
|
@@ -474,6 +474,25 @@
|
|
|
474
474
|
100% { transform: translateX(100%); }
|
|
475
475
|
}
|
|
476
476
|
|
|
477
|
+
/* Fade-in for animated text replacement */
|
|
478
|
+
@keyframes fade-in {
|
|
479
|
+
from { opacity: 0; transform: translateY(2px); }
|
|
480
|
+
to { opacity: 1; transform: translateY(0); }
|
|
481
|
+
}
|
|
482
|
+
.animate-fade-in {
|
|
483
|
+
animation: fade-in 0.3s ease-out;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Pulse-slide for indeterminate progress bars */
|
|
487
|
+
@keyframes pulse-slide {
|
|
488
|
+
0% { transform: translateX(-100%); opacity: 0.4; }
|
|
489
|
+
50% { transform: translateX(100%); opacity: 1; }
|
|
490
|
+
100% { transform: translateX(300%); opacity: 0.4; }
|
|
491
|
+
}
|
|
492
|
+
.animate-pulse-slide {
|
|
493
|
+
animation: pulse-slide 2s ease-in-out infinite;
|
|
494
|
+
}
|
|
495
|
+
|
|
477
496
|
/* Card exit (ghost card deletion) */
|
|
478
497
|
@keyframes card-exit {
|
|
479
498
|
0% {
|
|
@@ -545,6 +564,362 @@ body {
|
|
|
545
564
|
outline-offset: 2px;
|
|
546
565
|
}
|
|
547
566
|
|
|
567
|
+
/* ─── Book Reader Container (theme backgrounds) ─── */
|
|
568
|
+
.book-reader-container {
|
|
569
|
+
background: var(--background);
|
|
570
|
+
color: var(--foreground);
|
|
571
|
+
}
|
|
572
|
+
[data-book-theme="sepia"].book-reader-container {
|
|
573
|
+
background: oklch(0.95 0.03 75);
|
|
574
|
+
color: oklch(0.25 0.04 60);
|
|
575
|
+
}
|
|
576
|
+
[data-book-theme="dark"].book-reader-container {
|
|
577
|
+
background: oklch(0.14 0.02 250);
|
|
578
|
+
color: oklch(0.88 0.01 250);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/* Theme preview buttons in settings */
|
|
582
|
+
.book-theme-preview-sepia:not([class*="border-primary"]) {
|
|
583
|
+
background: oklch(0.95 0.03 75);
|
|
584
|
+
}
|
|
585
|
+
.book-theme-preview-dark:not([class*="border-primary"]) {
|
|
586
|
+
background: oklch(0.18 0.02 250);
|
|
587
|
+
color: oklch(0.88 0.01 250);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* ─── Book Reader Prose ─── */
|
|
591
|
+
.book-prose {
|
|
592
|
+
& p { margin-bottom: 1.25em; }
|
|
593
|
+
& blockquote {
|
|
594
|
+
border-left: 3px solid var(--border);
|
|
595
|
+
padding-left: 1em;
|
|
596
|
+
margin: 1.5em 0;
|
|
597
|
+
color: var(--muted-foreground);
|
|
598
|
+
font-style: italic;
|
|
599
|
+
}
|
|
600
|
+
& h2 {
|
|
601
|
+
font-size: 1.5em;
|
|
602
|
+
font-weight: 700;
|
|
603
|
+
margin-top: 2.5em;
|
|
604
|
+
margin-bottom: 0.75em;
|
|
605
|
+
letter-spacing: -0.01em;
|
|
606
|
+
}
|
|
607
|
+
& h3 {
|
|
608
|
+
font-size: 1.25em;
|
|
609
|
+
font-weight: 600;
|
|
610
|
+
margin-top: 2em;
|
|
611
|
+
margin-bottom: 0.75em;
|
|
612
|
+
}
|
|
613
|
+
& h4 {
|
|
614
|
+
font-size: 1.1em;
|
|
615
|
+
font-weight: 600;
|
|
616
|
+
margin-top: 1.75em;
|
|
617
|
+
margin-bottom: 0.5em;
|
|
618
|
+
}
|
|
619
|
+
& ul, & ol {
|
|
620
|
+
margin: 1em 0;
|
|
621
|
+
padding-left: 1.5em;
|
|
622
|
+
}
|
|
623
|
+
& ul { list-style-type: disc; }
|
|
624
|
+
& ol { list-style-type: decimal; }
|
|
625
|
+
& li { margin-bottom: 0.4em; }
|
|
626
|
+
& li > ul, & li > ol {
|
|
627
|
+
margin-top: 0.25em;
|
|
628
|
+
margin-bottom: 0.25em;
|
|
629
|
+
}
|
|
630
|
+
& strong { font-weight: 650; }
|
|
631
|
+
& em { font-style: italic; }
|
|
632
|
+
& code {
|
|
633
|
+
font-family: var(--font-mono);
|
|
634
|
+
font-size: 0.875em;
|
|
635
|
+
padding: 0.15em 0.35em;
|
|
636
|
+
border-radius: 4px;
|
|
637
|
+
background: var(--muted);
|
|
638
|
+
}
|
|
639
|
+
& a {
|
|
640
|
+
color: var(--primary);
|
|
641
|
+
text-decoration: underline;
|
|
642
|
+
text-underline-offset: 2px;
|
|
643
|
+
transition: opacity 0.15s;
|
|
644
|
+
&:hover { opacity: 0.8; }
|
|
645
|
+
}
|
|
646
|
+
& hr {
|
|
647
|
+
border: none;
|
|
648
|
+
border-top: 1px solid var(--border);
|
|
649
|
+
margin: 2em 0;
|
|
650
|
+
}
|
|
651
|
+
/* Tables */
|
|
652
|
+
& table {
|
|
653
|
+
width: 100%;
|
|
654
|
+
border-collapse: collapse;
|
|
655
|
+
margin: 1.5em 0;
|
|
656
|
+
font-size: 0.9em;
|
|
657
|
+
}
|
|
658
|
+
& thead th {
|
|
659
|
+
text-align: left;
|
|
660
|
+
font-weight: 600;
|
|
661
|
+
padding: 0.5em 0.75em;
|
|
662
|
+
border-bottom: 2px solid var(--border);
|
|
663
|
+
}
|
|
664
|
+
& tbody td {
|
|
665
|
+
padding: 0.5em 0.75em;
|
|
666
|
+
border-bottom: 1px solid var(--border);
|
|
667
|
+
vertical-align: top;
|
|
668
|
+
}
|
|
669
|
+
& tbody tr:last-child td { border-bottom: none; }
|
|
670
|
+
/* Images within markdown */
|
|
671
|
+
& img {
|
|
672
|
+
max-width: 100%;
|
|
673
|
+
border-radius: 8px;
|
|
674
|
+
margin: 1.5em auto;
|
|
675
|
+
display: block;
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
/* ─── Book Code Blocks (syntax-highlighted) ─── */
|
|
680
|
+
.book-code-header {
|
|
681
|
+
background: var(--muted);
|
|
682
|
+
border: 1px solid var(--border);
|
|
683
|
+
border-bottom: none;
|
|
684
|
+
}
|
|
685
|
+
.book-code-pre {
|
|
686
|
+
background: var(--surface-3);
|
|
687
|
+
border: 1px solid var(--border);
|
|
688
|
+
font-family: var(--font-mono);
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* sugar-high token colors — light theme */
|
|
692
|
+
.book-code-pre code {
|
|
693
|
+
--sh-class: oklch(0.55 0.22 260); /* indigo - classes */
|
|
694
|
+
--sh-identifier: var(--foreground); /* default text */
|
|
695
|
+
--sh-sign: oklch(0.45 0.03 250); /* punctuation */
|
|
696
|
+
--sh-property: oklch(0.50 0.17 260); /* properties */
|
|
697
|
+
--sh-entity: oklch(0.52 0.17 165); /* entities — emerald */
|
|
698
|
+
--sh-jsxliterals: oklch(0.55 0.19 45); /* JSX text — warm */
|
|
699
|
+
--sh-string: oklch(0.52 0.17 165); /* strings — emerald */
|
|
700
|
+
--sh-keyword: oklch(0.55 0.22 300); /* keywords — magenta */
|
|
701
|
+
--sh-comment: oklch(0.55 0.02 250); /* comments — muted */
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
/* sugar-high token colors — sepia reader theme */
|
|
705
|
+
[data-book-theme="sepia"] .book-code-header {
|
|
706
|
+
background: oklch(0.93 0.03 75);
|
|
707
|
+
border-color: oklch(0.85 0.04 75);
|
|
708
|
+
}
|
|
709
|
+
[data-book-theme="sepia"] .book-code-pre {
|
|
710
|
+
background: oklch(0.96 0.02 75);
|
|
711
|
+
border-color: oklch(0.85 0.04 75);
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
/* sugar-high token colors — dark reader theme */
|
|
715
|
+
[data-book-theme="dark"] .book-code-header {
|
|
716
|
+
background: oklch(0.20 0.02 250);
|
|
717
|
+
border-color: oklch(0.28 0.02 250);
|
|
718
|
+
}
|
|
719
|
+
[data-book-theme="dark"] .book-code-pre {
|
|
720
|
+
background: oklch(0.14 0.02 250);
|
|
721
|
+
border-color: oklch(0.28 0.02 250);
|
|
722
|
+
}
|
|
723
|
+
[data-book-theme="dark"] .book-code-pre code {
|
|
724
|
+
--sh-class: oklch(0.72 0.18 260);
|
|
725
|
+
--sh-identifier: oklch(0.88 0.01 250);
|
|
726
|
+
--sh-sign: oklch(0.60 0.02 250);
|
|
727
|
+
--sh-property: oklch(0.70 0.16 260);
|
|
728
|
+
--sh-entity: oklch(0.68 0.17 165);
|
|
729
|
+
--sh-jsxliterals: oklch(0.70 0.16 45);
|
|
730
|
+
--sh-string: oklch(0.68 0.17 165);
|
|
731
|
+
--sh-keyword: oklch(0.72 0.18 300);
|
|
732
|
+
--sh-comment: oklch(0.48 0.02 250);
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
/* system dark mode overrides for code blocks */
|
|
736
|
+
.dark .book-code-header {
|
|
737
|
+
background: oklch(0.20 0.02 250);
|
|
738
|
+
border-color: oklch(0.28 0.02 250);
|
|
739
|
+
}
|
|
740
|
+
.dark .book-code-pre {
|
|
741
|
+
background: oklch(0.14 0.02 250);
|
|
742
|
+
border-color: oklch(0.28 0.02 250);
|
|
743
|
+
}
|
|
744
|
+
.dark .book-code-pre code {
|
|
745
|
+
--sh-class: oklch(0.72 0.18 260);
|
|
746
|
+
--sh-identifier: oklch(0.88 0.01 250);
|
|
747
|
+
--sh-sign: oklch(0.60 0.02 250);
|
|
748
|
+
--sh-property: oklch(0.70 0.16 260);
|
|
749
|
+
--sh-entity: oklch(0.68 0.17 165);
|
|
750
|
+
--sh-jsxliterals: oklch(0.70 0.16 45);
|
|
751
|
+
--sh-string: oklch(0.68 0.17 165);
|
|
752
|
+
--sh-keyword: oklch(0.72 0.18 300);
|
|
753
|
+
--sh-comment: oklch(0.48 0.02 250);
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
/* ─── Book Image Blocks ─── */
|
|
757
|
+
.book-image {
|
|
758
|
+
border: 1px solid var(--border);
|
|
759
|
+
}
|
|
760
|
+
.book-image-breakout {
|
|
761
|
+
width: 170%;
|
|
762
|
+
max-width: min(170%, calc(100vw - 16rem));
|
|
763
|
+
margin-left: -35%;
|
|
764
|
+
}
|
|
765
|
+
.book-image-breakout figcaption {
|
|
766
|
+
width: 58.82%;
|
|
767
|
+
margin-left: 20.59%;
|
|
768
|
+
}
|
|
769
|
+
.book-image-skeleton {
|
|
770
|
+
background: var(--muted);
|
|
771
|
+
border: 1px solid var(--border);
|
|
772
|
+
}
|
|
773
|
+
[data-book-theme="sepia"] .book-image {
|
|
774
|
+
border-color: oklch(0.85 0.04 75);
|
|
775
|
+
}
|
|
776
|
+
[data-book-theme="sepia"] .book-image-skeleton {
|
|
777
|
+
background: oklch(0.93 0.03 75);
|
|
778
|
+
border-color: oklch(0.85 0.04 75);
|
|
779
|
+
}
|
|
780
|
+
[data-book-theme="dark"] .book-image {
|
|
781
|
+
border-color: oklch(0.28 0.02 250);
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
/* ─── Book Callout Blocks ─── */
|
|
785
|
+
.book-callout-tip {
|
|
786
|
+
border-left-color: oklch(0.52 0.17 165);
|
|
787
|
+
background: oklch(0.52 0.17 165 / 0.05);
|
|
788
|
+
}
|
|
789
|
+
.book-callout-tip .book-callout-icon { color: oklch(0.52 0.17 165); }
|
|
790
|
+
.book-callout-tip .book-callout-title { color: oklch(0.42 0.12 165); }
|
|
791
|
+
|
|
792
|
+
.book-callout-warning {
|
|
793
|
+
border-left-color: oklch(0.65 0.18 75);
|
|
794
|
+
background: oklch(0.65 0.18 75 / 0.05);
|
|
795
|
+
}
|
|
796
|
+
.book-callout-warning .book-callout-icon { color: oklch(0.65 0.18 75); }
|
|
797
|
+
.book-callout-warning .book-callout-title { color: oklch(0.55 0.14 75); }
|
|
798
|
+
|
|
799
|
+
.book-callout-info {
|
|
800
|
+
border-left-color: oklch(0.55 0.20 260);
|
|
801
|
+
background: oklch(0.55 0.20 260 / 0.05);
|
|
802
|
+
}
|
|
803
|
+
.book-callout-info .book-callout-icon { color: oklch(0.55 0.20 260); }
|
|
804
|
+
.book-callout-info .book-callout-title { color: oklch(0.45 0.16 260); }
|
|
805
|
+
|
|
806
|
+
.book-callout-lesson {
|
|
807
|
+
border-left-color: oklch(0.55 0.20 300);
|
|
808
|
+
background: oklch(0.55 0.20 300 / 0.05);
|
|
809
|
+
}
|
|
810
|
+
.book-callout-lesson .book-callout-icon { color: oklch(0.55 0.20 300); }
|
|
811
|
+
.book-callout-lesson .book-callout-title { color: oklch(0.45 0.16 300); }
|
|
812
|
+
|
|
813
|
+
|
|
814
|
+
/* callout body inline code */
|
|
815
|
+
.book-callout-body code {
|
|
816
|
+
background: oklch(0 0 0 / 0.05);
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
/* sepia callout overrides */
|
|
820
|
+
[data-book-theme="sepia"] .book-callout-tip { background: oklch(0.52 0.17 165 / 0.08); }
|
|
821
|
+
[data-book-theme="sepia"] .book-callout-warning { background: oklch(0.65 0.18 75 / 0.08); }
|
|
822
|
+
[data-book-theme="sepia"] .book-callout-info { background: oklch(0.55 0.20 260 / 0.08); }
|
|
823
|
+
[data-book-theme="sepia"] .book-callout-lesson { background: oklch(0.55 0.20 300 / 0.08); }
|
|
824
|
+
[data-book-theme="sepia"] .book-callout-body code { background: oklch(0 0 0 / 0.06); }
|
|
825
|
+
|
|
826
|
+
/* dark callout overrides */
|
|
827
|
+
[data-book-theme="dark"] .book-callout-tip {
|
|
828
|
+
background: oklch(0.52 0.17 165 / 0.10);
|
|
829
|
+
}
|
|
830
|
+
[data-book-theme="dark"] .book-callout-tip .book-callout-icon { color: oklch(0.68 0.17 165); }
|
|
831
|
+
[data-book-theme="dark"] .book-callout-tip .book-callout-title { color: oklch(0.72 0.14 165); }
|
|
832
|
+
|
|
833
|
+
[data-book-theme="dark"] .book-callout-warning {
|
|
834
|
+
background: oklch(0.65 0.18 75 / 0.10);
|
|
835
|
+
}
|
|
836
|
+
[data-book-theme="dark"] .book-callout-warning .book-callout-icon { color: oklch(0.75 0.18 75); }
|
|
837
|
+
[data-book-theme="dark"] .book-callout-warning .book-callout-title { color: oklch(0.80 0.14 75); }
|
|
838
|
+
|
|
839
|
+
[data-book-theme="dark"] .book-callout-info {
|
|
840
|
+
background: oklch(0.55 0.20 260 / 0.10);
|
|
841
|
+
}
|
|
842
|
+
[data-book-theme="dark"] .book-callout-info .book-callout-icon { color: oklch(0.70 0.18 260); }
|
|
843
|
+
[data-book-theme="dark"] .book-callout-info .book-callout-title { color: oklch(0.75 0.14 260); }
|
|
844
|
+
|
|
845
|
+
[data-book-theme="dark"] .book-callout-lesson {
|
|
846
|
+
background: oklch(0.55 0.20 300 / 0.10);
|
|
847
|
+
}
|
|
848
|
+
[data-book-theme="dark"] .book-callout-lesson .book-callout-icon { color: oklch(0.70 0.18 300); }
|
|
849
|
+
[data-book-theme="dark"] .book-callout-lesson .book-callout-title { color: oklch(0.75 0.14 300); }
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
[data-book-theme="dark"] .book-callout-body code { background: oklch(1 0 0 / 0.10); }
|
|
853
|
+
|
|
854
|
+
/* system dark mode callout overrides */
|
|
855
|
+
.dark .book-callout-tip .book-callout-icon { color: oklch(0.68 0.17 165); }
|
|
856
|
+
.dark .book-callout-tip .book-callout-title { color: oklch(0.72 0.14 165); }
|
|
857
|
+
.dark .book-callout-warning .book-callout-icon { color: oklch(0.75 0.18 75); }
|
|
858
|
+
.dark .book-callout-warning .book-callout-title { color: oklch(0.80 0.14 75); }
|
|
859
|
+
.dark .book-callout-info .book-callout-icon { color: oklch(0.70 0.18 260); }
|
|
860
|
+
.dark .book-callout-info .book-callout-title { color: oklch(0.75 0.14 260); }
|
|
861
|
+
.dark .book-callout-lesson .book-callout-icon { color: oklch(0.70 0.18 300); }
|
|
862
|
+
.dark .book-callout-lesson .book-callout-title { color: oklch(0.75 0.14 300); }
|
|
863
|
+
.dark .book-callout-body code { background: oklch(1 0 0 / 0.10); }
|
|
864
|
+
|
|
865
|
+
/* ─── Book Interactive Blocks ─── */
|
|
866
|
+
.book-interactive-link {
|
|
867
|
+
border-color: var(--border);
|
|
868
|
+
}
|
|
869
|
+
.book-interactive-link:hover {
|
|
870
|
+
border-color: var(--primary);
|
|
871
|
+
background: oklch(0.50 0.20 260 / 0.05);
|
|
872
|
+
}
|
|
873
|
+
.book-interactive-collapsible {
|
|
874
|
+
border-color: var(--border);
|
|
875
|
+
}
|
|
876
|
+
.book-interactive-quiz {
|
|
877
|
+
border-color: var(--border);
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
/* quiz answer states */
|
|
881
|
+
.book-quiz-correct {
|
|
882
|
+
border-color: oklch(0.52 0.17 165 / 0.5);
|
|
883
|
+
background: oklch(0.52 0.17 165 / 0.05);
|
|
884
|
+
color: oklch(0.42 0.12 165);
|
|
885
|
+
}
|
|
886
|
+
.book-quiz-wrong {
|
|
887
|
+
border-color: oklch(0.55 0.22 25 / 0.5);
|
|
888
|
+
background: oklch(0.55 0.22 25 / 0.05);
|
|
889
|
+
color: oklch(0.45 0.18 25);
|
|
890
|
+
}
|
|
891
|
+
.book-quiz-explanation-correct {
|
|
892
|
+
border-left-color: oklch(0.52 0.17 165 / 0.3);
|
|
893
|
+
background: oklch(0.52 0.17 165 / 0.05);
|
|
894
|
+
}
|
|
895
|
+
.book-quiz-explanation-wrong {
|
|
896
|
+
border-left-color: oklch(0.65 0.18 75 / 0.3);
|
|
897
|
+
background: oklch(0.65 0.18 75 / 0.05);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
/* dark reader quiz overrides */
|
|
901
|
+
[data-book-theme="dark"] .book-quiz-correct {
|
|
902
|
+
color: oklch(0.72 0.14 165);
|
|
903
|
+
}
|
|
904
|
+
[data-book-theme="dark"] .book-quiz-wrong {
|
|
905
|
+
color: oklch(0.72 0.16 25);
|
|
906
|
+
}
|
|
907
|
+
[data-book-theme="dark"] .book-interactive-link:hover {
|
|
908
|
+
background: oklch(0.65 0.20 260 / 0.10);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
/* dark reader interactive borders */
|
|
912
|
+
[data-book-theme="dark"] .book-interactive-link,
|
|
913
|
+
[data-book-theme="dark"] .book-interactive-collapsible,
|
|
914
|
+
[data-book-theme="dark"] .book-interactive-quiz {
|
|
915
|
+
border-color: oklch(0.28 0.02 250);
|
|
916
|
+
}
|
|
917
|
+
|
|
918
|
+
/* sepia interactive backgrounds */
|
|
919
|
+
[data-book-theme="sepia"] .book-interactive-link:hover {
|
|
920
|
+
background: oklch(0.50 0.20 260 / 0.06);
|
|
921
|
+
}
|
|
922
|
+
|
|
548
923
|
/* Reduced motion — disable all animations and transitions */
|
|
549
924
|
@media (prefers-reduced-motion: reduce) {
|
|
550
925
|
*,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { notFound } from "next/navigation";
|
|
2
2
|
import { db } from "@/lib/db";
|
|
3
|
-
import { projects, tasks } from "@/lib/db/schema";
|
|
4
|
-
import { eq, count } from "drizzle-orm";
|
|
3
|
+
import { projects, tasks, workflows } from "@/lib/db/schema";
|
|
4
|
+
import { eq, count, getTableColumns } from "drizzle-orm";
|
|
5
5
|
import { Badge } from "@/components/ui/badge";
|
|
6
6
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
7
7
|
import { COLUMN_ORDER } from "@/lib/constants/task-status";
|
|
@@ -28,26 +28,41 @@ export default async function ProjectDetailPage({
|
|
|
28
28
|
if (!project) notFound();
|
|
29
29
|
|
|
30
30
|
const projectTasks = await db
|
|
31
|
-
.select(
|
|
31
|
+
.select({
|
|
32
|
+
...getTableColumns(tasks),
|
|
33
|
+
workflowName: workflows.name,
|
|
34
|
+
workflowStatus: workflows.status,
|
|
35
|
+
})
|
|
32
36
|
.from(tasks)
|
|
37
|
+
.leftJoin(workflows, eq(tasks.workflowId, workflows.id))
|
|
33
38
|
.where(eq(tasks.projectId, id))
|
|
34
39
|
.orderBy(tasks.priority, tasks.createdAt);
|
|
35
40
|
|
|
36
|
-
// Status breakdown
|
|
41
|
+
// Status breakdown (standalone tasks only for headline metrics)
|
|
37
42
|
const statusCounts: Record<string, number> = {};
|
|
43
|
+
const standaloneForCounts = projectTasks.filter((t) => !t.workflowId);
|
|
38
44
|
for (const status of COLUMN_ORDER) {
|
|
39
|
-
statusCounts[status] =
|
|
45
|
+
statusCounts[status] = standaloneForCounts.filter((t) => t.status === status).length;
|
|
40
46
|
}
|
|
41
47
|
|
|
42
48
|
const completionTrend = await getProjectCompletionTrend(id, 14);
|
|
43
|
-
const totalTasks =
|
|
49
|
+
const totalTasks = standaloneForCounts.length;
|
|
50
|
+
|
|
51
|
+
const standaloneTasks = projectTasks.filter((t) => !t.workflowId);
|
|
52
|
+
const workflowTasks = projectTasks.filter((t) => t.workflowId);
|
|
44
53
|
|
|
45
54
|
const serializedTasks = projectTasks.map((t) => ({
|
|
46
55
|
...t,
|
|
47
56
|
createdAt: t.createdAt.toISOString(),
|
|
48
57
|
updatedAt: t.updatedAt.toISOString(),
|
|
58
|
+
workflowName: t.workflowName ?? null,
|
|
59
|
+
workflowStatus: t.workflowStatus ?? null,
|
|
49
60
|
}));
|
|
50
61
|
|
|
62
|
+
const standaloneCount = standaloneTasks.length;
|
|
63
|
+
const workflowCount = workflowTasks.length;
|
|
64
|
+
const workflowGroupCount = new Set(workflowTasks.map((t) => t.workflowId)).size;
|
|
65
|
+
|
|
51
66
|
const statusVariant: Record<string, "default" | "secondary" | "outline"> = {
|
|
52
67
|
active: "default",
|
|
53
68
|
paused: "secondary",
|
|
@@ -129,6 +144,16 @@ export default async function ProjectDetailPage({
|
|
|
129
144
|
</div>
|
|
130
145
|
)}
|
|
131
146
|
|
|
147
|
+
{/* Task count summary */}
|
|
148
|
+
{(standaloneCount > 0 || workflowCount > 0) && (
|
|
149
|
+
<p className="text-xs text-muted-foreground mb-4">
|
|
150
|
+
{standaloneCount} standalone task{standaloneCount !== 1 ? "s" : ""}
|
|
151
|
+
{workflowCount > 0 && (
|
|
152
|
+
<> · {workflowCount} workflow task{workflowCount !== 1 ? "s" : ""} across {workflowGroupCount} workflow{workflowGroupCount !== 1 ? "s" : ""}</>
|
|
153
|
+
)}
|
|
154
|
+
</p>
|
|
155
|
+
)}
|
|
156
|
+
|
|
132
157
|
<ProjectDetailClient tasks={serializedTasks} projectId={id} />
|
|
133
158
|
</PageShell>
|
|
134
159
|
);
|
|
@@ -4,6 +4,7 @@ import { PermissionsSections } from "@/components/settings/permissions-sections"
|
|
|
4
4
|
import { DataManagementSection } from "@/components/settings/data-management-section";
|
|
5
5
|
import { BudgetGuardrailsSection } from "@/components/settings/budget-guardrails-section";
|
|
6
6
|
import { ChatSettingsSection } from "@/components/settings/chat-settings-section";
|
|
7
|
+
import { RuntimeTimeoutSection } from "@/components/settings/runtime-timeout-section";
|
|
7
8
|
import { PageShell } from "@/components/shared/page-shell";
|
|
8
9
|
|
|
9
10
|
export const dynamic = "force-dynamic";
|
|
@@ -15,6 +16,7 @@ export default function SettingsPage() {
|
|
|
15
16
|
<AuthConfigSection />
|
|
16
17
|
<OpenAIRuntimeSection />
|
|
17
18
|
<ChatSettingsSection />
|
|
19
|
+
<RuntimeTimeoutSection />
|
|
18
20
|
<BudgetGuardrailsSection />
|
|
19
21
|
<PermissionsSections />
|
|
20
22
|
<DataManagementSection />
|
|
@@ -3,6 +3,8 @@ import { getDocBySlug, getManifest } from "@/lib/docs/reader";
|
|
|
3
3
|
import { getAdoptionMap } from "@/lib/docs/adoption";
|
|
4
4
|
import { PageShell } from "@/components/shared/page-shell";
|
|
5
5
|
import { PlaybookDetailView } from "@/components/playbook/playbook-detail-view";
|
|
6
|
+
import { getChapterForDoc } from "@/lib/book/chapter-mapping";
|
|
7
|
+
import { getChapter } from "@/lib/book/content";
|
|
6
8
|
|
|
7
9
|
export const dynamic = "force-dynamic";
|
|
8
10
|
|
|
@@ -15,8 +17,8 @@ export async function generateMetadata({ params }: PlaybookDetailProps) {
|
|
|
15
17
|
const doc = getDocBySlug(slug);
|
|
16
18
|
return {
|
|
17
19
|
title: doc
|
|
18
|
-
? `${(doc.frontmatter.title as string) || slug} |
|
|
19
|
-
: "Not Found |
|
|
20
|
+
? `${(doc.frontmatter.title as string) || slug} | User Guide | Stagent`
|
|
21
|
+
: "Not Found | User Guide",
|
|
20
22
|
};
|
|
21
23
|
}
|
|
22
24
|
|
|
@@ -54,6 +56,13 @@ export default async function PlaybookDetailPage({
|
|
|
54
56
|
|
|
55
57
|
const adoption = Object.fromEntries(adoptionMap);
|
|
56
58
|
|
|
59
|
+
// Find related book chapter for "Read the story" breadcrumb
|
|
60
|
+
const bookChapterId = getChapterForDoc(slug);
|
|
61
|
+
const bookChapter = bookChapterId ? getChapter(bookChapterId) : undefined;
|
|
62
|
+
const bookChapterLink = bookChapter && bookChapterId
|
|
63
|
+
? { title: `Ch. ${bookChapter.number}: ${bookChapter.title}`, id: bookChapterId }
|
|
64
|
+
: undefined;
|
|
65
|
+
|
|
57
66
|
// Collect all known doc slugs so markdown links resolve correctly
|
|
58
67
|
const allSlugs = [
|
|
59
68
|
...manifest.sections.map((s) => s.slug),
|
|
@@ -69,6 +78,7 @@ export default async function PlaybookDetailPage({
|
|
|
69
78
|
relatedSections={relatedSections}
|
|
70
79
|
adoption={adoption}
|
|
71
80
|
allSlugs={allSlugs}
|
|
81
|
+
bookChapterLink={bookChapterLink}
|
|
72
82
|
/>
|
|
73
83
|
</PageShell>
|
|
74
84
|
);
|
|
@@ -9,7 +9,7 @@ import { PageShell } from "@/components/shared/page-shell";
|
|
|
9
9
|
export const dynamic = "force-dynamic";
|
|
10
10
|
|
|
11
11
|
export const metadata = {
|
|
12
|
-
title: "
|
|
12
|
+
title: "User Guide | Stagent",
|
|
13
13
|
};
|
|
14
14
|
|
|
15
15
|
export default async function PlaybookPage() {
|
|
@@ -40,7 +40,7 @@ export default async function PlaybookPage() {
|
|
|
40
40
|
new Date(lastGenerated) > new Date(lastVisit);
|
|
41
41
|
|
|
42
42
|
return (
|
|
43
|
-
<PageShell title="
|
|
43
|
+
<PageShell title="User Guide">
|
|
44
44
|
<PlaybookHomepage
|
|
45
45
|
manifest={manifest}
|
|
46
46
|
stage={stage}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { notFound } from "next/navigation";
|
|
2
|
+
import Link from "next/link";
|
|
2
3
|
import { db } from "@/lib/db";
|
|
3
|
-
import { workflows } from "@/lib/db/schema";
|
|
4
|
+
import { workflows, projects } from "@/lib/db/schema";
|
|
4
5
|
import { eq } from "drizzle-orm";
|
|
5
6
|
import { PageShell } from "@/components/shared/page-shell";
|
|
6
7
|
import { WorkflowStatusView } from "@/components/workflows/workflow-status-view";
|
|
8
|
+
import { Badge } from "@/components/ui/badge";
|
|
9
|
+
import { FolderKanban } from "lucide-react";
|
|
7
10
|
|
|
8
11
|
export const dynamic = "force-dynamic";
|
|
9
12
|
|
|
@@ -21,8 +24,31 @@ export default async function WorkflowDetailPage({
|
|
|
21
24
|
|
|
22
25
|
if (!workflow) notFound();
|
|
23
26
|
|
|
27
|
+
// Fetch project name for back-link
|
|
28
|
+
let projectName: string | null = null;
|
|
29
|
+
if (workflow.projectId) {
|
|
30
|
+
const [project] = await db
|
|
31
|
+
.select({ name: projects.name })
|
|
32
|
+
.from(projects)
|
|
33
|
+
.where(eq(projects.id, workflow.projectId));
|
|
34
|
+
projectName = project?.name ?? null;
|
|
35
|
+
}
|
|
36
|
+
|
|
24
37
|
return (
|
|
25
|
-
<PageShell
|
|
38
|
+
<PageShell
|
|
39
|
+
backHref="/workflows"
|
|
40
|
+
backLabel="Back to Workflows"
|
|
41
|
+
actions={
|
|
42
|
+
workflow.projectId && projectName ? (
|
|
43
|
+
<Link href={`/projects/${workflow.projectId}`}>
|
|
44
|
+
<Badge variant="outline" className="gap-1.5 hover:bg-accent">
|
|
45
|
+
<FolderKanban className="h-3 w-3" />
|
|
46
|
+
{projectName}
|
|
47
|
+
</Badge>
|
|
48
|
+
</Link>
|
|
49
|
+
) : undefined
|
|
50
|
+
}
|
|
51
|
+
>
|
|
26
52
|
<WorkflowStatusView workflowId={id} />
|
|
27
53
|
</PageShell>
|
|
28
54
|
);
|