@mevdragon/vidfarm-devcli 0.16.0 → 0.18.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/.agents/skills/farmville-saas-ux/SKILL.md +156 -0
- package/.agents/skills/farmville-saas-ux/assets/starter.html +294 -0
- package/.agents/skills/farmville-saas-ux/references/components.md +340 -0
- package/.agents/skills/farmville-saas-ux/references/porting-guide.md +121 -0
- package/.agents/skills/farmville-saas-ux/references/tokens.md +271 -0
- package/.agents/skills/vidfarm-media/SKILL.md +4 -3
- package/.agents/skills/vidfarm-media/references/tts.md +20 -1
- package/SKILL.director.md +20 -20
- package/SKILL.platform.md +4 -4
- package/dist/src/account-pages-legacy.js +2 -2
- package/dist/src/app.js +721 -101
- package/dist/src/cli.js +11 -10
- package/dist/src/devcli/clips.js +64 -64
- package/dist/src/editor-chat.js +2 -2
- package/dist/src/frontend/homepage-client.js +162 -2
- package/dist/src/frontend/homepage-store.js +30 -1
- package/dist/src/frontend/homepage-view.js +111 -4
- package/dist/src/homepage.js +184 -1
- package/dist/src/landing-page.js +367 -0
- package/dist/src/primitive-registry.js +278 -2
- package/dist/src/reskin/agency-page.js +299 -0
- package/dist/src/reskin/calendar-page.js +567 -0
- package/dist/src/reskin/chat-page.js +607 -0
- package/dist/src/reskin/discover-page.js +1096 -0
- package/dist/src/reskin/document.js +663 -0
- package/dist/src/reskin/help-page.js +356 -0
- package/dist/src/reskin/index-page.js +62 -0
- package/dist/src/reskin/inpaint-page.js +541 -0
- package/dist/src/reskin/job-runs-page.js +477 -0
- package/dist/src/reskin/library-page.js +688 -0
- package/dist/src/reskin/login-page.js +262 -0
- package/dist/src/reskin/pricing-page.js +388 -0
- package/dist/src/reskin/settings-page.js +687 -0
- package/dist/src/reskin/theme.js +362 -0
- package/dist/src/services/serverless-records.js +54 -0
- package/dist/src/services/swipe-customize.js +434 -0
- package/package.json +1 -1
- package/public/assets/homepage-client-app.js +22 -22
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
// /reskin/chat — the AI brainstorm chat, MIGRATED from a static mockup into a
|
|
2
|
+
// REAL working chat wired to the live editor-chat backend.
|
|
3
|
+
//
|
|
4
|
+
// Like settings-page.ts, this renderer accepts the SAME input object the live
|
|
5
|
+
// chat route already builds (`Parameters<typeof renderChatPage>[0]`). The
|
|
6
|
+
// `input.editorChat` boot carries the REAL streaming endpoint, the vidfarm API
|
|
7
|
+
// key, the template context, and the cached context the agent needs — we embed
|
|
8
|
+
// that boot as JSON in the page and the composer POSTs straight to it.
|
|
9
|
+
//
|
|
10
|
+
// What's REAL: the composer sends the user's message to the real editor-chat
|
|
11
|
+
// endpoint (`boot.apiUrl`, with the `vidfarm-api-key` header + same-origin
|
|
12
|
+
// creds + the correct body shape) and renders the streamed assistant reply
|
|
13
|
+
// (SSE `text-delta` events). What's SIMPLIFIED: tool calls are rendered as
|
|
14
|
+
// plain status notes (no rich artifact/HTTP cards), attachments are not wired,
|
|
15
|
+
// and there is no thread-history sidebar (the boot carries no history). The
|
|
16
|
+
// design stays farmville — shared rk-* primitives + a page-scoped rk-chat-
|
|
17
|
+
// block + one self-contained script. No app.ts imports.
|
|
18
|
+
import { reskinDocument, rkEscape } from "./document.js";
|
|
19
|
+
// Default keeps the current zero-arg route (`renderReskinChat()`) compiling.
|
|
20
|
+
// With no editorChat boot the composer degrades gracefully: it renders the
|
|
21
|
+
// user bubble + a friendly "endpoint not available" reply instead of throwing.
|
|
22
|
+
const DEFAULT_INPUT = {
|
|
23
|
+
userId: "",
|
|
24
|
+
currentAccountId: "",
|
|
25
|
+
name: null,
|
|
26
|
+
email: "",
|
|
27
|
+
editorChat: null
|
|
28
|
+
};
|
|
29
|
+
// ── icons ───────────────────────────────────────────────────────────────────
|
|
30
|
+
const SEND = `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M4 12l16-8-6 16-2.5-6.5L4 12Z"/></svg>`;
|
|
31
|
+
// Full-sentence starter prompts — clicking one sends it straight to the agent.
|
|
32
|
+
const STARTERS = [
|
|
33
|
+
"Pitch me 5 scroll-stopping hooks I can test on cold TikTok traffic.",
|
|
34
|
+
"Turn my best idea into a ready-to-shoot UGC ad script.",
|
|
35
|
+
"Remix a trending short-form format for my product.",
|
|
36
|
+
"Write word-by-word captions for a 24-second talking-head cut."
|
|
37
|
+
];
|
|
38
|
+
export function renderReskinChat(input = DEFAULT_INPUT) {
|
|
39
|
+
const boot = input.editorChat ?? null;
|
|
40
|
+
// Embed the real boot as JSON. Escaping only `<` is the correct (and
|
|
41
|
+
// sufficient) way to keep a `<script type="application/json">` block from
|
|
42
|
+
// breaking out; HTML-entity escaping would corrupt JSON.parse.
|
|
43
|
+
const bootJson = JSON.stringify(boot).replace(/</g, "\\u003c");
|
|
44
|
+
const starterChips = STARTERS.map((s) => `<button type="button" class="rk-chat-chip" data-rk-prompt="${rkEscape(s)}">${rkEscape(s)}</button>`).join("");
|
|
45
|
+
const body = `
|
|
46
|
+
<main class="rk-chat-page">
|
|
47
|
+
<aside class="rk-chat-history" id="rk-chat-history" aria-label="Chat history">
|
|
48
|
+
<div class="rk-chat-history-head">
|
|
49
|
+
<span class="rk-chat-history-title">Chats</span>
|
|
50
|
+
<button type="button" class="rk-chat-newbtn" id="rk-chat-new"><span class="rk-chat-plus" aria-hidden="true">+</span> New</button>
|
|
51
|
+
</div>
|
|
52
|
+
<div class="rk-chat-history-list" id="rk-chat-history-list"></div>
|
|
53
|
+
</aside>
|
|
54
|
+
<div class="rk-chat-histbackdrop" id="rk-chat-histbackdrop" hidden></div>
|
|
55
|
+
<div class="rk-chat-main">
|
|
56
|
+
<div class="rk-chat-col">
|
|
57
|
+
<header class="rk-chat-head">
|
|
58
|
+
<button type="button" class="rk-chat-histtoggle" id="rk-chat-histtoggle" aria-label="Show chat history">☰</button>
|
|
59
|
+
<div class="rk-chat-head-titles">
|
|
60
|
+
<span class="rk-chat-eyebrow">Brainstorm</span>
|
|
61
|
+
<h1 class="rk-chat-title" id="rk-chat-title">Brainstorm studio</h1>
|
|
62
|
+
<p class="rk-chat-subhead">Short-form angles, hooks & scripts — brainstormed live on your own AI keys, never billed by vidfarm.</p>
|
|
63
|
+
</div>
|
|
64
|
+
<div class="rk-chat-head-actions">
|
|
65
|
+
<label class="rk-chat-mode" aria-label="Studio mode">
|
|
66
|
+
<select class="rk-chat-mode-select" id="rk-chat-mode">
|
|
67
|
+
<option value="/chat" selected>Brainstorm</option>
|
|
68
|
+
<option value="/inpaint">Create Image</option>
|
|
69
|
+
</select>
|
|
70
|
+
<span class="rk-chat-mode-chev" aria-hidden="true">▾</span>
|
|
71
|
+
</label>
|
|
72
|
+
</div>
|
|
73
|
+
</header>
|
|
74
|
+
|
|
75
|
+
<div class="rk-chat-thread" id="rk-chat-thread">
|
|
76
|
+
<div class="rk-chat-msg is-assistant">
|
|
77
|
+
<div class="rk-chat-meta"><span class="rk-chat-avatar" aria-hidden="true">V</span>Copilot</div>
|
|
78
|
+
<div class="rk-chat-bubble rk-chat-bubble-assistant">
|
|
79
|
+
<div class="rk-chat-stream">Tell me the offer, who it’s for, and where the traffic is cold — I’ll come back with angles, hooks, and scripts you can render. Pick a starter below or just start typing.</div>
|
|
80
|
+
</div>
|
|
81
|
+
</div>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="rk-chat-dock">
|
|
85
|
+
<div class="rk-chat-starters" aria-label="Starter prompts">
|
|
86
|
+
${starterChips}
|
|
87
|
+
</div>
|
|
88
|
+
<div class="rk-chat-composer" id="rk-chat-composer">
|
|
89
|
+
<textarea id="rk-chat-input" class="rk-chat-input" rows="1"
|
|
90
|
+
placeholder="Describe your offer, audience, or the problem to solve…"></textarea>
|
|
91
|
+
<button type="button" class="rk-btn rk-btn-gold rk-chat-send" id="rk-chat-send">Send ${SEND}</button>
|
|
92
|
+
</div>
|
|
93
|
+
<p class="rk-chat-hint">Brainstormed on your own AI keys — never billed by vidfarm. <kbd>Enter</kbd> to send · <kbd>Shift</kbd>+<kbd>Enter</kbd> for a new line.</p>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</div>
|
|
97
|
+
<script type="application/json" id="rk-chat-boot">${bootJson}</script>
|
|
98
|
+
</main>`;
|
|
99
|
+
const pageCss = `
|
|
100
|
+
/* ── two-pane: history sidebar + centered chat column ─────────────────────── */
|
|
101
|
+
.rk-chat-page{display:flex;width:100%;background:var(--rk-bg);min-height:100vh}
|
|
102
|
+
.rk-chat-main{flex:1;min-width:0;display:flex;justify-content:center;padding:0 var(--rk-gutter)}
|
|
103
|
+
.rk-chat-col{width:min(800px,100%);margin:0 auto;display:flex;flex-direction:column;
|
|
104
|
+
min-height:100vh;padding-top:34px}
|
|
105
|
+
|
|
106
|
+
/* history sidebar (real editor-chat threads) */
|
|
107
|
+
.rk-chat-history{flex:none;width:266px;background:var(--rk-surface);border-right:1px solid var(--rk-border);
|
|
108
|
+
display:flex;flex-direction:column;position:sticky;top:0;height:100vh}
|
|
109
|
+
.rk-chat-history-head{display:flex;align-items:center;justify-content:space-between;gap:10px;
|
|
110
|
+
padding:20px 16px 12px;flex:none}
|
|
111
|
+
.rk-chat-history-title{font-family:var(--rk-font-display);font-weight:800;font-size:16px;color:var(--rk-ink);letter-spacing:-.01em}
|
|
112
|
+
.rk-chat-newbtn{display:inline-flex;align-items:center;gap:5px;padding:7px 13px;border-radius:var(--rk-r-full);
|
|
113
|
+
border:1px solid var(--rk-gold-600);background:var(--rk-gold-tint);color:var(--rk-ink);cursor:pointer;
|
|
114
|
+
font-family:var(--rk-font-body);font-size:12.5px;font-weight:700;transition:background var(--rk-dur) var(--rk-ease)}
|
|
115
|
+
.rk-chat-newbtn:hover{background:var(--rk-gold-tint2)}
|
|
116
|
+
.rk-chat-history-list{flex:1;min-height:0;overflow-y:auto;padding:4px 10px 16px;display:grid;gap:2px;align-content:start}
|
|
117
|
+
.rk-chat-hrow{display:flex;align-items:center;gap:8px;width:100%;padding:9px 10px;border-radius:var(--rk-r-lg);
|
|
118
|
+
border:0;background:transparent;cursor:pointer;text-align:left;font-family:inherit;min-width:0;
|
|
119
|
+
transition:background var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease)}
|
|
120
|
+
.rk-chat-hrow:hover{background:var(--rk-n-50);box-shadow:var(--rk-shadow-xs)}
|
|
121
|
+
.rk-chat-hrow.is-active{background:var(--rk-gold-tint)}
|
|
122
|
+
.rk-chat-hrow-main{flex:1;min-width:0;display:grid;gap:1px}
|
|
123
|
+
.rk-chat-hrow-title{font-size:13px;font-weight:600;color:var(--rk-ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
124
|
+
.rk-chat-hrow-sub{font-size:11px;color:var(--rk-text-muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
|
|
125
|
+
.rk-chat-hrow-del{flex:none;border:0;background:transparent;color:var(--rk-n-400);cursor:pointer;font-size:15px;
|
|
126
|
+
line-height:1;padding:2px 5px;border-radius:var(--rk-r-sm);opacity:0;
|
|
127
|
+
transition:opacity var(--rk-dur) var(--rk-ease),color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
128
|
+
.rk-chat-hrow:hover .rk-chat-hrow-del{opacity:1}
|
|
129
|
+
.rk-chat-hrow-del:hover{color:var(--rk-red);background:var(--rk-red-tint)}
|
|
130
|
+
.rk-chat-history-note{padding:16px 14px;font-size:12.5px;color:var(--rk-text-muted);line-height:1.55;display:grid;gap:9px;justify-items:start}
|
|
131
|
+
.rk-chat-history-note a{color:var(--rk-gold-700);font-weight:600}
|
|
132
|
+
.rk-chat-history-note button{font-family:inherit;font-size:12px;font-weight:600;color:var(--rk-ink);cursor:pointer;
|
|
133
|
+
background:#fff;border:1px solid var(--rk-border);border-radius:var(--rk-r-full);padding:5px 12px}
|
|
134
|
+
.rk-chat-history-note button:hover{background:var(--rk-n-100)}
|
|
135
|
+
|
|
136
|
+
/* narrow-screen history toggle + slide-over (hidden on wide) */
|
|
137
|
+
.rk-chat-histtoggle{display:none;flex:none;width:34px;height:34px;border-radius:var(--rk-r-lg);
|
|
138
|
+
border:1px solid var(--rk-border);background:#fff;color:var(--rk-n-600);cursor:pointer;font-size:15px;
|
|
139
|
+
align-items:center;justify-content:center;margin-right:2px;margin-top:2px}
|
|
140
|
+
.rk-chat-histbackdrop{display:none}
|
|
141
|
+
|
|
142
|
+
/* compact header */
|
|
143
|
+
.rk-chat-head{display:flex;align-items:flex-start;gap:12px;
|
|
144
|
+
padding-bottom:22px;margin-bottom:6px;border-bottom:1px solid var(--rk-border)}
|
|
145
|
+
.rk-chat-head-titles{display:grid;gap:6px;min-width:0;flex:1}
|
|
146
|
+
.rk-chat-eyebrow{display:inline-flex;align-items:center;font-size:12px;font-weight:700;
|
|
147
|
+
letter-spacing:.06em;text-transform:uppercase;color:var(--rk-gold-700)}
|
|
148
|
+
.rk-chat-title{font-family:var(--rk-font-display);font-weight:800;font-size:1.7rem;letter-spacing:-.025em;
|
|
149
|
+
line-height:1.15;color:var(--rk-ink);min-width:0}
|
|
150
|
+
.rk-chat-subhead{font-size:14.5px;color:var(--rk-text-muted);line-height:1.5;max-width:56ch}
|
|
151
|
+
.rk-chat-head-actions{display:flex;align-items:center;gap:9px;flex:none}
|
|
152
|
+
.rk-chat-plus{font-size:14px;font-weight:700;line-height:1;margin-right:1px}
|
|
153
|
+
|
|
154
|
+
/* studio mode select (Brainstorm ↔ Create Image → /reskin/inpaint) */
|
|
155
|
+
.rk-chat-mode{position:relative;display:inline-flex;align-items:center}
|
|
156
|
+
.rk-chat-mode-select{appearance:none;-webkit-appearance:none;font-family:var(--rk-font-body);font-size:13px;font-weight:700;
|
|
157
|
+
color:var(--rk-ink);background:var(--rk-surface);border:1px solid var(--rk-border-strong);border-radius:var(--rk-r-full);
|
|
158
|
+
padding:9px 32px 9px 15px;cursor:pointer;box-shadow:var(--rk-shadow-xs);
|
|
159
|
+
transition:border-color var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease)}
|
|
160
|
+
.rk-chat-mode-select:hover{border-color:var(--rk-gold-600)}
|
|
161
|
+
.rk-chat-mode-select:focus{outline:none;border-color:var(--rk-gold-600);box-shadow:var(--rk-ring-gold)}
|
|
162
|
+
.rk-chat-mode-chev{position:absolute;right:13px;top:50%;transform:translateY(-50%);pointer-events:none;font-size:11px;color:var(--rk-n-500)}
|
|
163
|
+
|
|
164
|
+
/* thread — flex:1 pushes the dock to the bottom when the page is short */
|
|
165
|
+
.rk-chat-thread{flex:1;display:grid;gap:28px;align-content:start;padding:30px 0 26px}
|
|
166
|
+
.rk-chat-msg{display:grid;gap:8px;min-width:0}
|
|
167
|
+
.rk-chat-msg.is-user{justify-items:end}
|
|
168
|
+
.rk-chat-meta{display:flex;align-items:center;gap:7px;font-size:11.5px;font-weight:700;letter-spacing:.05em;
|
|
169
|
+
text-transform:uppercase;color:var(--rk-text-faint);padding:0 4px}
|
|
170
|
+
.rk-chat-time{font-weight:600;letter-spacing:.02em;color:var(--rk-n-300);text-transform:none;font-size:11px}
|
|
171
|
+
.rk-chat-avatar{width:20px;height:20px;border-radius:6px;flex:none;display:grid;place-items:center;color:#fff;
|
|
172
|
+
font-family:var(--rk-font-display);font-weight:800;font-size:11px;line-height:1;
|
|
173
|
+
background:linear-gradient(142deg,var(--rk-gold-500),var(--rk-violet))}
|
|
174
|
+
|
|
175
|
+
/* bubbles — uniform 1px hairline on all four sides, no colored side stripes */
|
|
176
|
+
.rk-chat-bubble{border-radius:var(--rk-r-2xl);padding:16px 20px;font-size:15px;line-height:1.65}
|
|
177
|
+
.rk-chat-bubble-user{max-width:min(78%,560px);background:var(--rk-ink);color:#f4f4f5;
|
|
178
|
+
border:1px solid var(--rk-n-800);box-shadow:var(--rk-shadow-sm)}
|
|
179
|
+
.rk-chat-bubble-assistant{width:100%;background:var(--rk-surface);color:var(--rk-n-700);
|
|
180
|
+
border:1px solid var(--rk-border);box-shadow:var(--rk-shadow-card)}
|
|
181
|
+
.rk-chat-bubble-assistant strong{color:var(--rk-ink);font-weight:700}
|
|
182
|
+
|
|
183
|
+
/* streamed assistant text — pre-wrap keeps the model's own line breaks */
|
|
184
|
+
.rk-chat-stream{white-space:pre-wrap;overflow-wrap:anywhere;color:var(--rk-n-700);line-height:1.65}
|
|
185
|
+
.rk-chat-stream[hidden]{display:none}
|
|
186
|
+
|
|
187
|
+
/* thinking / status row */
|
|
188
|
+
.rk-chat-status{display:flex;align-items:center;gap:10px;color:var(--rk-text-faint);font-size:13.5px}
|
|
189
|
+
.rk-chat-status[hidden]{display:none}
|
|
190
|
+
.rk-chat-status-label{font-size:13px;color:var(--rk-text-muted)}
|
|
191
|
+
|
|
192
|
+
/* simplified tool-call notes (rich artifact/HTTP cards are intentionally omitted) */
|
|
193
|
+
.rk-chat-notes{display:grid;gap:6px;margin-bottom:10px}
|
|
194
|
+
.rk-chat-notes:empty{display:none}
|
|
195
|
+
.rk-chat-toolnote{display:inline-flex;align-items:center;gap:7px;font-size:12.5px;color:var(--rk-text-muted);
|
|
196
|
+
font-family:var(--rk-font-mono);background:var(--rk-n-50);border:1px solid var(--rk-border);
|
|
197
|
+
border-radius:var(--rk-r-lg);padding:5px 10px;width:fit-content}
|
|
198
|
+
.rk-chat-toolnote::before{content:"";width:6px;height:6px;border-radius:50%;background:var(--rk-gold-500);flex:none}
|
|
199
|
+
|
|
200
|
+
/* error surface */
|
|
201
|
+
.rk-chat-error{margin-top:2px;font-size:13.5px;color:#b42318;background:var(--rk-n-50);
|
|
202
|
+
border:1px solid var(--rk-border);border-radius:var(--rk-r-lg);padding:10px 12px;white-space:pre-wrap}
|
|
203
|
+
|
|
204
|
+
/* typing indicator */
|
|
205
|
+
.rk-chat-typing{display:inline-flex;gap:4px;align-items:center;padding:2px 0}
|
|
206
|
+
.rk-chat-typing span{width:7px;height:7px;border-radius:50%;background:var(--rk-n-400);
|
|
207
|
+
animation:rk-chat-blink 1.2s var(--rk-ease) infinite}
|
|
208
|
+
.rk-chat-typing span:nth-child(2){animation-delay:.18s}
|
|
209
|
+
.rk-chat-typing span:nth-child(3){animation-delay:.36s}
|
|
210
|
+
@keyframes rk-chat-blink{0%,80%,100%{opacity:.28;transform:translateY(0)}40%{opacity:1;transform:translateY(-2px)}}
|
|
211
|
+
|
|
212
|
+
/* ── quiet sticky composer dock ── */
|
|
213
|
+
.rk-chat-dock{position:sticky;bottom:0;z-index:10;background:var(--rk-bg);
|
|
214
|
+
padding:16px 0 22px;display:grid;gap:12px}
|
|
215
|
+
.rk-chat-dock::before{content:"";position:absolute;left:0;right:0;top:-26px;height:26px;pointer-events:none;
|
|
216
|
+
background:linear-gradient(to top,var(--rk-bg),transparent)}
|
|
217
|
+
.rk-chat-starters{display:flex;flex-wrap:wrap;gap:9px}
|
|
218
|
+
.rk-chat-chip{display:inline-flex;align-items:center;gap:7px;padding:8px 14px;border-radius:var(--rk-r-full);
|
|
219
|
+
font-family:var(--rk-font-body);font-size:13px;font-weight:600;color:var(--rk-n-700);cursor:pointer;
|
|
220
|
+
background:var(--rk-surface);border:1px solid var(--rk-border);box-shadow:var(--rk-shadow-xs);text-align:left;
|
|
221
|
+
transition:transform var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease),border-color var(--rk-dur) var(--rk-ease),background var(--rk-dur) var(--rk-ease)}
|
|
222
|
+
.rk-chat-chip:hover{border-color:var(--rk-gold-600);background:var(--rk-gold-tint);color:var(--rk-ink);
|
|
223
|
+
transform:translateY(-1px);box-shadow:var(--rk-shadow-sm)}
|
|
224
|
+
.rk-chat-chip:disabled{opacity:.5;cursor:default;transform:none;box-shadow:var(--rk-shadow-xs)}
|
|
225
|
+
.rk-chat-composer{display:flex;align-items:flex-end;gap:9px;background:var(--rk-surface);
|
|
226
|
+
border:1px solid var(--rk-border-strong);border-radius:var(--rk-r-3xl);padding:9px 9px 9px 16px;
|
|
227
|
+
box-shadow:var(--rk-shadow-card);transition:border-color var(--rk-dur) var(--rk-ease),box-shadow var(--rk-dur) var(--rk-ease)}
|
|
228
|
+
.rk-chat-composer:focus-within{border-color:var(--rk-gold-600);box-shadow:var(--rk-ring-gold)}
|
|
229
|
+
.rk-chat-input{flex:1;min-width:0;border:0;background:transparent;resize:none;font-family:var(--rk-font-body);
|
|
230
|
+
font-size:15px;line-height:1.5;color:var(--rk-ink);padding:10px 2px;max-height:148px}
|
|
231
|
+
.rk-chat-input:focus{outline:none;box-shadow:none}
|
|
232
|
+
.rk-chat-input::placeholder{color:var(--rk-n-400)}
|
|
233
|
+
.rk-chat-send{flex:none;padding:11px 18px}
|
|
234
|
+
.rk-chat-send svg{width:16px;height:16px}
|
|
235
|
+
.rk-chat-send:disabled{opacity:.55;cursor:default}
|
|
236
|
+
.rk-chat-hint{text-align:center;font-size:11.5px;color:var(--rk-text-faint);line-height:1.5}
|
|
237
|
+
.rk-chat-hint kbd{font-family:var(--rk-font-mono);font-size:10.5px;background:var(--rk-n-100);
|
|
238
|
+
border:1px solid var(--rk-border);border-radius:5px;padding:1px 5px;color:var(--rk-n-600)}
|
|
239
|
+
|
|
240
|
+
/* ── narrow: history becomes a slide-over, bubbles go full-width ── */
|
|
241
|
+
@media(max-width:900px){
|
|
242
|
+
.rk-chat-histtoggle{display:inline-flex}
|
|
243
|
+
.rk-chat-history{position:fixed;left:0;top:0;bottom:0;height:auto;z-index:70;width:min(300px,86vw);
|
|
244
|
+
transform:translateX(-100%);box-shadow:var(--rk-shadow-xl);transition:transform var(--rk-dur) var(--rk-ease)}
|
|
245
|
+
.rk-chat-history.is-open{transform:none}
|
|
246
|
+
.rk-chat-histbackdrop{display:block;position:fixed;inset:0;z-index:65;background:rgba(0,0,0,.28)}
|
|
247
|
+
.rk-chat-histbackdrop[hidden]{display:none}
|
|
248
|
+
}
|
|
249
|
+
@media(max-width:720px){
|
|
250
|
+
.rk-chat-col{padding-top:24px}
|
|
251
|
+
.rk-chat-title{font-size:1.5rem}
|
|
252
|
+
.rk-chat-bubble-user{max-width:100%}
|
|
253
|
+
.rk-chat-thread{gap:22px}
|
|
254
|
+
}
|
|
255
|
+
`;
|
|
256
|
+
const script = `
|
|
257
|
+
(function(){
|
|
258
|
+
var root=document;
|
|
259
|
+
|
|
260
|
+
// ── read the real editor-chat boot (endpoint + auth + context) ──────────
|
|
261
|
+
var boot=null;
|
|
262
|
+
try{ var bootEl=root.getElementById('rk-chat-boot'); if(bootEl){ boot=JSON.parse(bootEl.textContent||'null'); } }catch(e){ boot=null; }
|
|
263
|
+
var ENDPOINT=(boot && boot.apiUrl) || '/api/v1/editor-chat';
|
|
264
|
+
var API_KEY=(boot && boot.vidfarmApiKey) || null;
|
|
265
|
+
var TEMPLATE=(boot && boot.template) || null;
|
|
266
|
+
var CACHED=(boot && boot.cachedContext) || null;
|
|
267
|
+
|
|
268
|
+
var input=root.getElementById('rk-chat-input');
|
|
269
|
+
var send=root.getElementById('rk-chat-send');
|
|
270
|
+
var thread=root.getElementById('rk-chat-thread');
|
|
271
|
+
var title=root.getElementById('rk-chat-title');
|
|
272
|
+
var AVATAR='V';
|
|
273
|
+
|
|
274
|
+
var history=[]; // [{role, text}] — the running conversation
|
|
275
|
+
var threadId=null; // generated on first send, reused for persistence
|
|
276
|
+
var busy=false;
|
|
277
|
+
|
|
278
|
+
function genId(p){ return p+'-'+Date.now().toString(36)+'-'+Math.random().toString(36).slice(2,8); }
|
|
279
|
+
function autosize(){ if(!input)return; input.style.height='auto'; input.style.height=Math.min(input.scrollHeight,148)+'px'; }
|
|
280
|
+
function toBottom(){ try{ window.scrollTo({top:document.body.scrollHeight,behavior:'smooth'}); }catch(e){ window.scrollTo(0,document.body.scrollHeight); } }
|
|
281
|
+
function nowLabel(){ try{ return new Date().toLocaleTimeString([], {hour:'numeric', minute:'2-digit'}); }catch(e){ return ''; } }
|
|
282
|
+
function el(cls){ var d=root.createElement('div'); d.className=cls; return d; }
|
|
283
|
+
function setBusy(v){ busy=v; if(send){ send.disabled=v; } root.querySelectorAll('[data-rk-prompt]').forEach(function(c){ c.disabled=v; }); }
|
|
284
|
+
|
|
285
|
+
function clientContext(){
|
|
286
|
+
var now=new Date(); var local=null, tz=null;
|
|
287
|
+
try{ local=new Intl.DateTimeFormat(undefined,{dateStyle:'full',timeStyle:'long'}).format(now); }catch(e){ local=now.toString(); }
|
|
288
|
+
try{ tz=(Intl.DateTimeFormat().resolvedOptions().timeZone)||null; }catch(e){ tz=null; }
|
|
289
|
+
return { currentDateTime: now.toISOString(), localDateTime: local, timeZone: tz };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function appendUser(text){
|
|
293
|
+
var msg=el('rk-chat-msg is-user');
|
|
294
|
+
var meta=el('rk-chat-meta'); meta.innerHTML='You <span class="rk-chat-time">'+nowLabel()+'</span>';
|
|
295
|
+
var bub=el('rk-chat-bubble rk-chat-bubble-user'); bub.textContent=text;
|
|
296
|
+
msg.appendChild(meta); msg.appendChild(bub);
|
|
297
|
+
if(thread) thread.appendChild(msg);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Returns a small controller for the streaming assistant bubble.
|
|
301
|
+
function appendAssistant(){
|
|
302
|
+
var msg=el('rk-chat-msg is-assistant');
|
|
303
|
+
var meta=el('rk-chat-meta');
|
|
304
|
+
meta.innerHTML='<span class="rk-chat-avatar" aria-hidden="true">'+AVATAR+'</span>Copilot <span class="rk-chat-time">'+nowLabel()+'</span>';
|
|
305
|
+
var bub=el('rk-chat-bubble rk-chat-bubble-assistant');
|
|
306
|
+
var status=el('rk-chat-status');
|
|
307
|
+
status.innerHTML='<span class="rk-chat-typing"><span></span><span></span><span></span></span><span class="rk-chat-status-label">Thinking…</span>';
|
|
308
|
+
var notes=el('rk-chat-notes');
|
|
309
|
+
var textEl=el('rk-chat-stream'); textEl.hidden=true;
|
|
310
|
+
bub.appendChild(status); bub.appendChild(notes); bub.appendChild(textEl);
|
|
311
|
+
msg.appendChild(meta); msg.appendChild(bub);
|
|
312
|
+
if(thread) thread.appendChild(msg);
|
|
313
|
+
return {
|
|
314
|
+
setStatus:function(s){ var l=status.querySelector('.rk-chat-status-label'); if(l && s){ l.textContent=s; } },
|
|
315
|
+
hideStatus:function(){ status.hidden=true; },
|
|
316
|
+
showText:function(){ status.hidden=true; textEl.hidden=false; },
|
|
317
|
+
setText:function(t){ textEl.textContent=t; },
|
|
318
|
+
addNote:function(t){ var n=el('rk-chat-toolnote'); n.textContent=t; notes.appendChild(n); },
|
|
319
|
+
fail:function(m){ status.hidden=true; var er=el('rk-chat-error'); er.textContent=m||'Something went wrong.'; bub.appendChild(er); }
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Minimal SSE reader — mirrors the live client's text/event-stream parsing.
|
|
324
|
+
function readStream(response, h){
|
|
325
|
+
if(!response.body || !response.body.getReader){
|
|
326
|
+
// No streaming reader available — fall back to reading the whole body.
|
|
327
|
+
return response.text().then(function(t){ parseEvents(t, h); });
|
|
328
|
+
}
|
|
329
|
+
var reader=response.body.getReader();
|
|
330
|
+
var decoder=new TextDecoder();
|
|
331
|
+
var buffer='';
|
|
332
|
+
function pump(){
|
|
333
|
+
return reader.read().then(function(res){
|
|
334
|
+
if(res.done){ if(buffer){ parseEvents(buffer, h); buffer=''; } return; }
|
|
335
|
+
buffer+=decoder.decode(res.value,{stream:true});
|
|
336
|
+
var events=buffer.split('\\n\\n');
|
|
337
|
+
buffer=events.pop()||'';
|
|
338
|
+
for(var i=0;i<events.length;i++){ handleEvent(events[i], h); }
|
|
339
|
+
return pump();
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
return pump();
|
|
343
|
+
}
|
|
344
|
+
function parseEvents(blob, h){ blob.split('\\n\\n').forEach(function(ev){ handleEvent(ev, h); }); }
|
|
345
|
+
function handleEvent(raw, h){
|
|
346
|
+
var lines=raw.split('\\n');
|
|
347
|
+
var dataLine=null;
|
|
348
|
+
for(var j=0;j<lines.length;j++){ var t=lines[j].trim(); if(t.indexOf('data:')===0){ dataLine=t; break; } }
|
|
349
|
+
if(!dataLine) return;
|
|
350
|
+
var payload=dataLine.slice(5).trim();
|
|
351
|
+
if(payload==='[DONE]' || !payload) return;
|
|
352
|
+
var chunk; try{ chunk=JSON.parse(payload); }catch(e){ return; }
|
|
353
|
+
if(!chunk || typeof chunk!=='object') return;
|
|
354
|
+
if(chunk.type==='text-delta' && typeof chunk.delta==='string'){ h.onDelta(chunk.delta); }
|
|
355
|
+
else if(chunk.type==='status'){ var s=(typeof chunk.status==='string')?chunk.status:((typeof chunk.message==='string')?chunk.message:null); if(s){ h.onStatus(s); } }
|
|
356
|
+
else if(chunk.type==='tool-call' && typeof chunk.toolName==='string'){ h.onToolCall(chunk.toolName); }
|
|
357
|
+
else if(chunk.type==='error'){ h.onError((typeof chunk.errorText==='string')?chunk.errorText:((chunk.error && chunk.error.message)||'Unable to read assistant response.')); }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
function sendMessage(text){
|
|
361
|
+
if(busy) return;
|
|
362
|
+
text=(text||'').trim();
|
|
363
|
+
if(!text) return;
|
|
364
|
+
|
|
365
|
+
history.push({role:'user', text:text});
|
|
366
|
+
appendUser(text);
|
|
367
|
+
if(input){ input.value=''; autosize(); }
|
|
368
|
+
if(title && history.length===1){ title.textContent=text.slice(0,60); }
|
|
369
|
+
toBottom();
|
|
370
|
+
|
|
371
|
+
var view=appendAssistant();
|
|
372
|
+
toBottom();
|
|
373
|
+
setBusy(true);
|
|
374
|
+
|
|
375
|
+
if(!threadId){ threadId=genId('thread'); }
|
|
376
|
+
var userMsgId=genId('user');
|
|
377
|
+
var asstMsgId=genId('assistant');
|
|
378
|
+
|
|
379
|
+
var body={
|
|
380
|
+
messages: history.map(function(m){ return { role:m.role, content:[{ type:'text', text:m.text }] }; }),
|
|
381
|
+
thread_id: threadId,
|
|
382
|
+
thread_template_id: (TEMPLATE && TEMPLATE.templateId) || 'chat-brainstorm',
|
|
383
|
+
thread_title: text.slice(0,42),
|
|
384
|
+
thread_tracers: (TEMPLATE && TEMPLATE.tracers) || [],
|
|
385
|
+
client_context: clientContext(),
|
|
386
|
+
user_message: { id:userMsgId, role:'user', text:text, attachments:[] },
|
|
387
|
+
assistant_message: { id:asstMsgId }
|
|
388
|
+
};
|
|
389
|
+
if(TEMPLATE){ body.template=TEMPLATE; }
|
|
390
|
+
if(CACHED){ body.cached_context=CACHED; }
|
|
391
|
+
|
|
392
|
+
var headers={ 'content-type':'application/json' };
|
|
393
|
+
if(API_KEY){ headers['vidfarm-api-key']=API_KEY; }
|
|
394
|
+
|
|
395
|
+
var acc='';
|
|
396
|
+
var sawText=false;
|
|
397
|
+
var errored=false;
|
|
398
|
+
|
|
399
|
+
var handlers={
|
|
400
|
+
onDelta:function(d){ if(!sawText){ sawText=true; view.showText(); } acc+=d; view.setText(acc); toBottom(); },
|
|
401
|
+
onStatus:function(s){ if(!sawText){ view.setStatus(s); } },
|
|
402
|
+
onToolCall:function(name){ view.addNote('Running '+name+'…'); toBottom(); },
|
|
403
|
+
onError:function(m){ errored=true; view.fail(m); toBottom(); }
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
var req;
|
|
407
|
+
try{
|
|
408
|
+
req=fetch(ENDPOINT,{ method:'POST', headers:headers, credentials:'same-origin', body:JSON.stringify(body) });
|
|
409
|
+
}catch(err){
|
|
410
|
+
// Synchronous fetch failure (e.g. invalid URL) — never let it throw uncaught.
|
|
411
|
+
req=Promise.reject(err);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
req.then(function(resp){
|
|
415
|
+
if(!resp.ok){
|
|
416
|
+
return resp.json().catch(function(){ return {}; }).then(function(j){
|
|
417
|
+
throw new Error((j && j.error) || ('Chat request failed ('+resp.status+').'));
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
return readStream(resp, handlers);
|
|
421
|
+
}).then(function(){
|
|
422
|
+
view.hideStatus();
|
|
423
|
+
if(sawText){
|
|
424
|
+
history.push({role:'assistant', text:acc});
|
|
425
|
+
} else if(!errored){
|
|
426
|
+
view.showText();
|
|
427
|
+
view.setText('The assistant finished without returning any text.');
|
|
428
|
+
}
|
|
429
|
+
setBusy(false);
|
|
430
|
+
if(input){ input.focus(); }
|
|
431
|
+
toBottom();
|
|
432
|
+
loadHistory();
|
|
433
|
+
}).catch(function(err){
|
|
434
|
+
var msg=(err && err.message) ? err.message : String(err);
|
|
435
|
+
if(!API_KEY){ msg=msg+'\\n\\nAdd an AI provider key in Settings to brainstorm on your own keys.'; }
|
|
436
|
+
view.fail(msg);
|
|
437
|
+
setBusy(false);
|
|
438
|
+
if(input){ input.focus(); }
|
|
439
|
+
toBottom();
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// ── real chat history (editor-chat threads) ────────────────────────────
|
|
444
|
+
var THREADS_URL=(boot && boot.threadsUrl) || '/api/v1/editor-chat/threads';
|
|
445
|
+
var TEMPLATE_ID=(TEMPLATE && TEMPLATE.templateId) || 'chat-brainstorm';
|
|
446
|
+
var histList=root.getElementById('rk-chat-history-list');
|
|
447
|
+
var histAside=root.getElementById('rk-chat-history');
|
|
448
|
+
var histBackdrop=root.getElementById('rk-chat-histbackdrop');
|
|
449
|
+
var histToggle=root.getElementById('rk-chat-histtoggle');
|
|
450
|
+
var loadingThread=false;
|
|
451
|
+
|
|
452
|
+
function authHeaders(extra){ var h=extra||{}; if(API_KEY){ h['vidfarm-api-key']=API_KEY; } return h; }
|
|
453
|
+
function relTime(iso){
|
|
454
|
+
if(!iso) return '';
|
|
455
|
+
var t=Date.parse(iso); if(isNaN(t)) return '';
|
|
456
|
+
var s=Math.max(0,(Date.now()-t)/1000);
|
|
457
|
+
if(s<60) return 'just now';
|
|
458
|
+
if(s<3600) return Math.floor(s/60)+'m ago';
|
|
459
|
+
if(s<86400) return Math.floor(s/3600)+'h ago';
|
|
460
|
+
if(s<604800) return Math.floor(s/86400)+'d ago';
|
|
461
|
+
try{ return new Date(t).toLocaleDateString([], {month:'short', day:'numeric'}); }catch(e){ return ''; }
|
|
462
|
+
}
|
|
463
|
+
function histNote(text, opts){
|
|
464
|
+
var wrap=el('rk-chat-history-note');
|
|
465
|
+
var p=root.createElement('div'); p.textContent=text; wrap.appendChild(p);
|
|
466
|
+
if(opts && opts.href){ var a=root.createElement('a'); a.href=opts.href; a.textContent=opts.linkText||'Open'; wrap.appendChild(a); }
|
|
467
|
+
if(opts && opts.retry){ var b=root.createElement('button'); b.type='button'; b.textContent='Try again'; b.addEventListener('click',function(){ loadHistory(); }); wrap.appendChild(b); }
|
|
468
|
+
return wrap;
|
|
469
|
+
}
|
|
470
|
+
function setActiveThread(id){
|
|
471
|
+
threadId=id;
|
|
472
|
+
if(!histList) return;
|
|
473
|
+
var rows=histList.querySelectorAll('.rk-chat-hrow');
|
|
474
|
+
for(var i=0;i<rows.length;i++){ rows[i].classList.toggle('is-active', rows[i].getAttribute('data-id')===id); }
|
|
475
|
+
}
|
|
476
|
+
function renderHistory(threads){
|
|
477
|
+
if(!histList) return;
|
|
478
|
+
histList.innerHTML='';
|
|
479
|
+
if(!threads.length){ histList.appendChild(histNote('No conversations yet. Start one below \\u2014 it saves automatically.')); return; }
|
|
480
|
+
threads.forEach(function(t){
|
|
481
|
+
var row=root.createElement('button');
|
|
482
|
+
row.type='button'; row.className='rk-chat-hrow'; row.setAttribute('data-id', t.id);
|
|
483
|
+
if(t.id===threadId){ row.classList.add('is-active'); }
|
|
484
|
+
var main=el('rk-chat-hrow-main');
|
|
485
|
+
var ti=el('rk-chat-hrow-title'); ti.textContent=t.title || 'Untitled chat';
|
|
486
|
+
var sub=el('rk-chat-hrow-sub');
|
|
487
|
+
var count=(typeof t.messageCount==='number')?t.messageCount:0;
|
|
488
|
+
var when=relTime(t.lastMessageAt || t.updatedAt);
|
|
489
|
+
sub.textContent=(count?count+' message'+(count===1?'':'s'):'')+((count&&when)?' \\u00b7 ':'')+(when||'');
|
|
490
|
+
main.appendChild(ti); main.appendChild(sub);
|
|
491
|
+
var del=root.createElement('button');
|
|
492
|
+
del.type='button'; del.className='rk-chat-hrow-del'; del.setAttribute('aria-label','Delete chat'); del.textContent='\\u00d7';
|
|
493
|
+
del.addEventListener('click', function(ev){ ev.stopPropagation(); deleteThread(t.id, row); });
|
|
494
|
+
row.appendChild(main); row.appendChild(del);
|
|
495
|
+
row.addEventListener('click', function(){ openThread(t.id); });
|
|
496
|
+
histList.appendChild(row);
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
function loadHistory(){
|
|
500
|
+
if(!histList) return;
|
|
501
|
+
if(!API_KEY){ histList.innerHTML=''; histList.appendChild(histNote('Add an AI provider key in Settings to sync your saved chats.', {href:'/settings', linkText:'Open Settings'})); return; }
|
|
502
|
+
var url=THREADS_URL+'?template_id='+encodeURIComponent(TEMPLATE_ID)+'&limit=30&include_messages=false';
|
|
503
|
+
fetch(url, { headers:authHeaders(), credentials:'same-origin' })
|
|
504
|
+
.then(function(r){
|
|
505
|
+
if(r.status===401||r.status===403){ return {threads:[], _auth:true}; }
|
|
506
|
+
if(!r.ok) throw new Error('http '+r.status);
|
|
507
|
+
return r.json();
|
|
508
|
+
})
|
|
509
|
+
.then(function(j){
|
|
510
|
+
if(j && j._auth){ histList.innerHTML=''; histList.appendChild(histNote('Sign in to sync your saved chats.', {href:'/login', linkText:'Sign in'})); return; }
|
|
511
|
+
renderHistory((j && j.threads) || []);
|
|
512
|
+
})
|
|
513
|
+
.catch(function(){ if(histList){ histList.innerHTML=''; histList.appendChild(histNote('Couldn\\u2019t load your chats.', {retry:true})); } });
|
|
514
|
+
}
|
|
515
|
+
function appendAssistantStatic(text){ var v=appendAssistant(); v.showText(); v.setText(text||''); v.hideStatus(); return v; }
|
|
516
|
+
function openThread(id){
|
|
517
|
+
if(busy || loadingThread) return;
|
|
518
|
+
loadingThread=true;
|
|
519
|
+
closeHistoryOverlay();
|
|
520
|
+
fetch(THREADS_URL+'/'+encodeURIComponent(id), { headers:authHeaders(), credentials:'same-origin' })
|
|
521
|
+
.then(function(r){ if(!r.ok) throw new Error('http '+r.status); return r.json(); })
|
|
522
|
+
.then(function(j){
|
|
523
|
+
var t=j && j.thread; if(!t){ throw new Error('missing thread'); }
|
|
524
|
+
history=[];
|
|
525
|
+
if(thread){ thread.innerHTML=''; }
|
|
526
|
+
var msgs=t.messages||[];
|
|
527
|
+
for(var i=0;i<msgs.length;i++){
|
|
528
|
+
var m=msgs[i]; var role=(m.role==='assistant')?'assistant':'user'; var txt=m.text||'';
|
|
529
|
+
history.push({role:role, text:txt});
|
|
530
|
+
if(role==='user'){ appendUser(txt); } else { appendAssistantStatic(txt); }
|
|
531
|
+
}
|
|
532
|
+
if(title){ title.textContent=t.title || 'Brainstorm studio'; }
|
|
533
|
+
setActiveThread(id);
|
|
534
|
+
loadingThread=false;
|
|
535
|
+
toBottom();
|
|
536
|
+
})
|
|
537
|
+
.catch(function(){ loadingThread=false; });
|
|
538
|
+
}
|
|
539
|
+
function deleteThread(id, row){
|
|
540
|
+
if(!API_KEY) return;
|
|
541
|
+
fetch(THREADS_URL+'/'+encodeURIComponent(id), { method:'DELETE', headers:authHeaders(), credentials:'same-origin' })
|
|
542
|
+
.then(function(r){
|
|
543
|
+
if(!r.ok && r.status!==404) throw new Error('http '+r.status);
|
|
544
|
+
if(row && row.parentNode){ row.parentNode.removeChild(row); }
|
|
545
|
+
if(id===threadId){ resetChat(); }
|
|
546
|
+
if(histList && !histList.querySelector('.rk-chat-hrow')){ loadHistory(); }
|
|
547
|
+
})
|
|
548
|
+
.catch(function(){});
|
|
549
|
+
}
|
|
550
|
+
function resetChat(){
|
|
551
|
+
history=[]; threadId=null;
|
|
552
|
+
if(thread){
|
|
553
|
+
thread.innerHTML='<div class="rk-chat-msg is-assistant">'+
|
|
554
|
+
'<div class="rk-chat-meta"><span class="rk-chat-avatar" aria-hidden="true">'+AVATAR+'</span>Copilot</div>'+
|
|
555
|
+
'<div class="rk-chat-bubble rk-chat-bubble-assistant"><div class="rk-chat-stream">Fresh page. Tell me the offer, who it\\u2019s for, and where the traffic is cold \\u2014 I\\u2019ll come back with angles, hooks, and scripts you can render.</div></div>'+
|
|
556
|
+
'</div>';
|
|
557
|
+
}
|
|
558
|
+
if(title){ title.textContent='Brainstorm studio'; }
|
|
559
|
+
setActiveThread(null);
|
|
560
|
+
if(input){ input.value=''; autosize(); input.focus(); }
|
|
561
|
+
window.scrollTo(0,0);
|
|
562
|
+
}
|
|
563
|
+
function openHistoryOverlay(){ if(histAside){ histAside.classList.add('is-open'); } if(histBackdrop){ histBackdrop.hidden=false; } }
|
|
564
|
+
function closeHistoryOverlay(){ if(histAside){ histAside.classList.remove('is-open'); } if(histBackdrop){ histBackdrop.hidden=true; } }
|
|
565
|
+
if(histToggle){ histToggle.addEventListener('click', openHistoryOverlay); }
|
|
566
|
+
if(histBackdrop){ histBackdrop.addEventListener('click', closeHistoryOverlay); }
|
|
567
|
+
loadHistory();
|
|
568
|
+
|
|
569
|
+
// composer wiring
|
|
570
|
+
if(input){ input.addEventListener('input',autosize); autosize(); }
|
|
571
|
+
if(send){ send.addEventListener('click',function(){ if(input){ sendMessage(input.value); } }); }
|
|
572
|
+
if(input){
|
|
573
|
+
input.addEventListener('keydown',function(ev){
|
|
574
|
+
if(ev.key==='Enter' && !ev.shiftKey){ ev.preventDefault(); sendMessage(input.value); }
|
|
575
|
+
});
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// starter chips send their full prompt directly
|
|
579
|
+
root.querySelectorAll('[data-rk-prompt]').forEach(function(chip){
|
|
580
|
+
chip.addEventListener('click',function(){ if(busy) return; sendMessage(chip.getAttribute('data-rk-prompt')||''); });
|
|
581
|
+
});
|
|
582
|
+
|
|
583
|
+
// "New" resets the conversation to the warm empty state (shared resetChat)
|
|
584
|
+
var newBtn=root.getElementById('rk-chat-new');
|
|
585
|
+
if(newBtn){
|
|
586
|
+
newBtn.addEventListener('click',function(){ if(busy) return; resetChat(); });
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
// studio mode: choosing "Create Image" navigates to the inpaint workbench
|
|
590
|
+
var modeSel=root.getElementById('rk-chat-mode');
|
|
591
|
+
if(modeSel){
|
|
592
|
+
modeSel.addEventListener('change',function(){
|
|
593
|
+
var dest=modeSel.value;
|
|
594
|
+
if(dest && dest!=='/chat'){ window.location.href=dest; }
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
})();`;
|
|
598
|
+
return reskinDocument({
|
|
599
|
+
title: "vidfarm reskin — Brainstorm",
|
|
600
|
+
description: "Reskinned vidfarm AI brainstorm chat, wired to the live editor-chat backend — a clean single-column conversation with starter chips and a working composer.",
|
|
601
|
+
activeSlug: "chat",
|
|
602
|
+
pageCss,
|
|
603
|
+
body,
|
|
604
|
+
script
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
//# sourceMappingURL=chat-page.js.map
|