jamdesk 1.0.13 ā 1.0.14
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 +89 -4
- package/dist/__tests__/unit/auth.test.d.ts +2 -0
- package/dist/__tests__/unit/auth.test.d.ts.map +1 -0
- package/dist/__tests__/unit/auth.test.js +169 -0
- package/dist/__tests__/unit/auth.test.js.map +1 -0
- package/dist/__tests__/unit/config.test.d.ts +2 -0
- package/dist/__tests__/unit/config.test.d.ts.map +1 -0
- package/dist/__tests__/unit/config.test.js +76 -0
- package/dist/__tests__/unit/config.test.js.map +1 -0
- package/dist/__tests__/unit/deploy.test.d.ts +2 -0
- package/dist/__tests__/unit/deploy.test.d.ts.map +1 -0
- package/dist/__tests__/unit/deploy.test.js +273 -0
- package/dist/__tests__/unit/deploy.test.js.map +1 -0
- package/dist/__tests__/unit/deps-sync.test.js +3 -1
- package/dist/__tests__/unit/deps-sync.test.js.map +1 -1
- package/dist/__tests__/unit/dev-loading-server.test.d.ts +2 -0
- package/dist/__tests__/unit/dev-loading-server.test.d.ts.map +1 -0
- package/dist/__tests__/unit/dev-loading-server.test.js +141 -0
- package/dist/__tests__/unit/dev-loading-server.test.js.map +1 -0
- package/dist/__tests__/unit/docs-json-writer.test.d.ts +2 -0
- package/dist/__tests__/unit/docs-json-writer.test.d.ts.map +1 -0
- package/dist/__tests__/unit/docs-json-writer.test.js +71 -0
- package/dist/__tests__/unit/docs-json-writer.test.js.map +1 -0
- package/dist/__tests__/unit/loading-page.test.d.ts +2 -0
- package/dist/__tests__/unit/loading-page.test.d.ts.map +1 -0
- package/dist/__tests__/unit/loading-page.test.js +73 -0
- package/dist/__tests__/unit/loading-page.test.js.map +1 -0
- package/dist/__tests__/unit/login.test.d.ts +2 -0
- package/dist/__tests__/unit/login.test.d.ts.map +1 -0
- package/dist/__tests__/unit/login.test.js +100 -0
- package/dist/__tests__/unit/login.test.js.map +1 -0
- package/dist/__tests__/unit/logout.test.d.ts +2 -0
- package/dist/__tests__/unit/logout.test.d.ts.map +1 -0
- package/dist/__tests__/unit/logout.test.js +39 -0
- package/dist/__tests__/unit/logout.test.js.map +1 -0
- package/dist/__tests__/unit/tarball.test.d.ts +2 -0
- package/dist/__tests__/unit/tarball.test.d.ts.map +1 -0
- package/dist/__tests__/unit/tarball.test.js +126 -0
- package/dist/__tests__/unit/tarball.test.js.map +1 -0
- package/dist/__tests__/unit/whoami.test.d.ts +2 -0
- package/dist/__tests__/unit/whoami.test.d.ts.map +1 -0
- package/dist/__tests__/unit/whoami.test.js +47 -0
- package/dist/__tests__/unit/whoami.test.js.map +1 -0
- package/dist/commands/deploy.d.ts +13 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +265 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +48 -25
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/login.d.ts +8 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +135 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/logout.d.ts +5 -0
- package/dist/commands/logout.d.ts.map +1 -0
- package/dist/commands/logout.js +17 -0
- package/dist/commands/logout.js.map +1 -0
- package/dist/commands/whoami.d.ts +5 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +50 -7
- package/dist/index.js.map +1 -1
- package/dist/lib/auth.d.ts +34 -0
- package/dist/lib/auth.d.ts.map +1 -0
- package/dist/lib/auth.js +105 -0
- package/dist/lib/auth.js.map +1 -0
- package/dist/lib/config.d.ts +9 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +7 -1
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/dev-loading-server.d.ts +22 -0
- package/dist/lib/dev-loading-server.d.ts.map +1 -0
- package/dist/lib/dev-loading-server.js +117 -0
- package/dist/lib/dev-loading-server.js.map +1 -0
- package/dist/lib/docs-config.d.ts +1 -0
- package/dist/lib/docs-config.d.ts.map +1 -1
- package/dist/lib/docs-config.js.map +1 -1
- package/dist/lib/docs-json-writer.d.ts +2 -0
- package/dist/lib/docs-json-writer.d.ts.map +1 -0
- package/dist/lib/docs-json-writer.js +35 -0
- package/dist/lib/docs-json-writer.js.map +1 -0
- package/dist/lib/loading-page.d.ts +11 -0
- package/dist/lib/loading-page.d.ts.map +1 -0
- package/dist/lib/loading-page.js +222 -0
- package/dist/lib/loading-page.js.map +1 -0
- package/dist/lib/output.d.ts +13 -5
- package/dist/lib/output.d.ts.map +1 -1
- package/dist/lib/output.js +22 -5
- package/dist/lib/output.js.map +1 -1
- package/dist/lib/tarball.d.ts +28 -0
- package/dist/lib/tarball.d.ts.map +1 -0
- package/dist/lib/tarball.js +117 -0
- package/dist/lib/tarball.js.map +1 -0
- package/package.json +5 -2
- package/vendored/app/[[...slug]]/page.tsx +6 -20
- package/vendored/app/api/chat/[project]/route.ts +323 -0
- package/vendored/app/api/mcp/[project]/route.ts +2 -63
- package/vendored/components/chat/ChatCodeBlock.tsx +63 -0
- package/vendored/components/chat/ChatEmptyState.tsx +79 -0
- package/vendored/components/chat/ChatFAB.tsx +36 -0
- package/vendored/components/chat/ChatInput.tsx +106 -0
- package/vendored/components/chat/ChatMessage.tsx +176 -0
- package/vendored/components/chat/ChatPanel.tsx +206 -0
- package/vendored/components/chat/ChatResizeHandle.tsx +108 -0
- package/vendored/components/chat/LazyChatPanel.tsx +19 -0
- package/vendored/components/layout/LayoutWrapper.tsx +134 -44
- package/vendored/components/layout/PageColumns.tsx +40 -0
- package/vendored/components/navigation/Header.tsx +74 -29
- package/vendored/components/navigation/Sidebar.tsx +17 -2
- package/vendored/hooks/useChat.ts +335 -0
- package/vendored/hooks/useChatPanel.tsx +101 -0
- package/vendored/lib/anthropic-client.ts +19 -0
- package/vendored/lib/build/extract-tarball.ts +150 -0
- package/vendored/lib/chat-prompt.ts +56 -0
- package/vendored/lib/docs-types.ts +14 -0
- package/vendored/lib/docs.ts +22 -4
- package/vendored/lib/embedding-chunker.ts +173 -0
- package/vendored/lib/generate-starter-questions.ts +98 -0
- package/vendored/lib/isr-build-executor.ts +2 -1
- package/vendored/lib/middleware-helpers.ts +21 -0
- package/vendored/lib/route-helpers.ts +96 -0
- package/vendored/lib/snippet-loader-isr.ts +107 -1
- package/vendored/lib/static-artifacts.ts +3 -2
- package/vendored/lib/validate-config.ts +1 -0
- package/vendored/lib/vector-store.ts +213 -0
- package/vendored/schema/docs-schema.json +33 -0
- package/vendored/scripts/dev-project.cjs +6 -0
- package/vendored/shared/types.ts +6 -5
- package/vendored/tailwind.config.ts +9 -0
- package/vendored/themes/jam/variables.css +2 -2
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstash Vector Store
|
|
3
|
+
*
|
|
4
|
+
* Manages vector embeddings for AI chat. Each project gets its own namespace
|
|
5
|
+
* in the Upstash Vector index. Upstash handles embedding generation
|
|
6
|
+
* server-side ā we send raw text, not pre-computed vectors.
|
|
7
|
+
*
|
|
8
|
+
* Used in two contexts:
|
|
9
|
+
* 1. Build time: upsertChunks() stores page chunks after a successful build
|
|
10
|
+
* 2. Chat API: querySimilarChunks() retrieves relevant context for user queries
|
|
11
|
+
*/
|
|
12
|
+
import { Index, FusionAlgorithm, WeightingStrategy, QueryMode } from '@upstash/vector';
|
|
13
|
+
import type { EmbeddingChunk } from './embedding-chunker.js';
|
|
14
|
+
|
|
15
|
+
export interface ChunkMetadata {
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
pageSlug: string;
|
|
18
|
+
sectionHeading: string;
|
|
19
|
+
pageTitle: string;
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Upstash limit per upsert call */
|
|
24
|
+
const BATCH_SIZE = 100;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Upstash metadata limit is 48KB per record. The content field in metadata
|
|
28
|
+
* is used for LLM context retrieval, while the `data` field (no limit) is
|
|
29
|
+
* used for embedding generation. Truncate metadata content to stay under limit.
|
|
30
|
+
*/
|
|
31
|
+
const MAX_METADATA_CONTENT_CHARS = 10000;
|
|
32
|
+
|
|
33
|
+
/** Hybrid search parameters ā DBSF fusion with IDF-weighted BM25 */
|
|
34
|
+
const HYBRID_QUERY_OPTS = {
|
|
35
|
+
queryMode: QueryMode.HYBRID,
|
|
36
|
+
fusionAlgorithm: FusionAlgorithm.DBSF,
|
|
37
|
+
weightingStrategy: WeightingStrategy.IDF,
|
|
38
|
+
} as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Minimum similarity score ā below this threshold results are near-random
|
|
42
|
+
* and would give the LLM poor context. Initially set for cosine similarity;
|
|
43
|
+
* may need recalibration after switching to DBSF/RRF fusion scores.
|
|
44
|
+
*/
|
|
45
|
+
const MIN_SCORE = 0.3;
|
|
46
|
+
|
|
47
|
+
/** Max chunks per page ā ensures diverse results across pages */
|
|
48
|
+
const MAX_CHUNKS_PER_PAGE = 3;
|
|
49
|
+
|
|
50
|
+
/** Create a namespaced Upstash Vector index for a project. */
|
|
51
|
+
function getNamespace(projectId: string) {
|
|
52
|
+
const index = new Index({
|
|
53
|
+
url: process.env.UPSTASH_VECTOR_REST_URL!,
|
|
54
|
+
token: process.env.UPSTASH_VECTOR_REST_TOKEN!,
|
|
55
|
+
});
|
|
56
|
+
return index.namespace(projectId);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Replace all vectors for a project with fresh chunks.
|
|
61
|
+
*
|
|
62
|
+
* Resets the namespace first (deletes all existing vectors), then upserts
|
|
63
|
+
* new chunks in batches of 100. Upstash generates embeddings from the
|
|
64
|
+
* `data` field ā no need for a separate embedding model.
|
|
65
|
+
*
|
|
66
|
+
* Note: This is NOT safe for concurrent calls on the same project ā reset()
|
|
67
|
+
* could clear another build's partially-upserted data. The build queue system
|
|
68
|
+
* guarantees one build per project at a time, preventing this race condition.
|
|
69
|
+
*/
|
|
70
|
+
export async function upsertChunks(
|
|
71
|
+
projectId: string,
|
|
72
|
+
chunks: EmbeddingChunk[],
|
|
73
|
+
): Promise<void> {
|
|
74
|
+
const ns = getNamespace(projectId);
|
|
75
|
+
|
|
76
|
+
await ns.reset();
|
|
77
|
+
|
|
78
|
+
for (let i = 0; i < chunks.length; i += BATCH_SIZE) {
|
|
79
|
+
const batch = chunks.slice(i, i + BATCH_SIZE);
|
|
80
|
+
await ns.upsert(
|
|
81
|
+
batch.map(c => ({
|
|
82
|
+
id: c.id,
|
|
83
|
+
data: c.content,
|
|
84
|
+
metadata: {
|
|
85
|
+
pageSlug: c.pageSlug,
|
|
86
|
+
sectionHeading: c.sectionHeading,
|
|
87
|
+
pageTitle: c.pageTitle,
|
|
88
|
+
content: c.content.length > MAX_METADATA_CONTENT_CHARS
|
|
89
|
+
? c.content.slice(0, MAX_METADATA_CONTENT_CHARS) + '...'
|
|
90
|
+
: c.content,
|
|
91
|
+
} satisfies ChunkMetadata,
|
|
92
|
+
})),
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Strip common question/request phrasing to extract the core topic.
|
|
99
|
+
* Long queries like "How do I make an analytics call - give me a javascript example"
|
|
100
|
+
* embed poorly ā the request-pattern words ("give me", "example") dilute the topic signal.
|
|
101
|
+
* Extracting just the topic ("analytics call") produces much better vector matches.
|
|
102
|
+
*
|
|
103
|
+
* Returns the stripped topic, or null if stripping didn't meaningfully shorten the query.
|
|
104
|
+
*/
|
|
105
|
+
export function extractTopicQuery(queryText: string): string | null {
|
|
106
|
+
let topic = queryText;
|
|
107
|
+
|
|
108
|
+
// Strip leading question patterns
|
|
109
|
+
topic = topic.replace(/^(how (do|can|would|should) (i|you|we)|can (i|you|we)|what('s| is| are)|where (do|can|is|are)|show me|tell me|give me|explain|describe|help me)\b/i, '');
|
|
110
|
+
|
|
111
|
+
// Strip trailing request patterns (after dash, comma, or period)
|
|
112
|
+
topic = topic.replace(/\s*[-āā,.]\s*(give me|show me|provide|include|with)\b.*$/i, '');
|
|
113
|
+
|
|
114
|
+
// Strip request-type suffixes
|
|
115
|
+
topic = topic.replace(/\b(give me|show me|provide|include)\s+(a|an|some|the)?\s*(javascript|typescript|python|code|curl)?\s*(example|sample|snippet|demo)s?\b/gi, '');
|
|
116
|
+
|
|
117
|
+
// Strip filler words
|
|
118
|
+
topic = topic.replace(/\b(please|just|basically|simply|actually)\b/gi, '');
|
|
119
|
+
|
|
120
|
+
// Clean up extra whitespace and trim
|
|
121
|
+
topic = topic.replace(/\s+/g, ' ').trim();
|
|
122
|
+
|
|
123
|
+
// Only use the topic query if it's meaningfully shorter (at least 30% shorter)
|
|
124
|
+
if (!topic || topic.length > queryText.length * 0.7) return null;
|
|
125
|
+
return topic;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/** Query with hybrid mode, falling back to dense-only if hybrid is not supported */
|
|
129
|
+
async function queryWithFallback(
|
|
130
|
+
ns: ReturnType<typeof getNamespace>,
|
|
131
|
+
params: { data: string; topK: number; includeMetadata: true },
|
|
132
|
+
): Promise<Array<{ id: string | number; score: number; metadata?: ChunkMetadata }>> {
|
|
133
|
+
try {
|
|
134
|
+
return await ns.query<ChunkMetadata>({ ...params, ...HYBRID_QUERY_OPTS });
|
|
135
|
+
} catch (err) {
|
|
136
|
+
// Hybrid not supported on this index ā fall back to dense-only
|
|
137
|
+
console.warn('[vector-store] Hybrid query failed, falling back to dense-only:', String(err));
|
|
138
|
+
return await ns.query<ChunkMetadata>({ ...params, queryMode: QueryMode.DENSE });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
type RawResult = { id: string | number; score: number; metadata?: ChunkMetadata };
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Deduplicate and filter raw vector results into scored chunks.
|
|
146
|
+
* Filters out low-score results and limits chunks per page for diversity.
|
|
147
|
+
* Accepts multiple result lists ā earlier lists take priority.
|
|
148
|
+
*/
|
|
149
|
+
function filterAndMerge(
|
|
150
|
+
resultSets: RawResult[][],
|
|
151
|
+
topK: number,
|
|
152
|
+
): Array<ChunkMetadata & { score: number }> {
|
|
153
|
+
const seen = new Set<string>();
|
|
154
|
+
const pageCount = new Map<string, number>();
|
|
155
|
+
const merged: Array<ChunkMetadata & { score: number }> = [];
|
|
156
|
+
|
|
157
|
+
for (const results of resultSets) {
|
|
158
|
+
for (const r of results) {
|
|
159
|
+
if (merged.length >= topK) return merged;
|
|
160
|
+
if (!r.metadata || r.score < MIN_SCORE) continue;
|
|
161
|
+
|
|
162
|
+
const id = String(r.id);
|
|
163
|
+
if (seen.has(id)) continue;
|
|
164
|
+
|
|
165
|
+
const count = pageCount.get(r.metadata.pageSlug) ?? 0;
|
|
166
|
+
if (count >= MAX_CHUNKS_PER_PAGE) continue;
|
|
167
|
+
|
|
168
|
+
seen.add(id);
|
|
169
|
+
pageCount.set(r.metadata.pageSlug, count + 1);
|
|
170
|
+
merged.push({ ...r.metadata, score: r.score });
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return merged;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Find chunks most similar to a query string.
|
|
179
|
+
*
|
|
180
|
+
* Uses a dual-query strategy for long queries: runs the full query AND an
|
|
181
|
+
* extracted topic-only version in parallel, then merges results. This prevents
|
|
182
|
+
* request-pattern words (e.g. "give me a javascript example") from diluting
|
|
183
|
+
* the topic signal in the embedding.
|
|
184
|
+
*
|
|
185
|
+
* Returns up to `topK` results with their similarity scores,
|
|
186
|
+
* filtering out any results with missing metadata.
|
|
187
|
+
*/
|
|
188
|
+
export async function querySimilarChunks(
|
|
189
|
+
projectId: string,
|
|
190
|
+
queryText: string,
|
|
191
|
+
topK = 5,
|
|
192
|
+
): Promise<Array<ChunkMetadata & { score: number }>> {
|
|
193
|
+
const ns = getNamespace(projectId);
|
|
194
|
+
const queryParams = { topK, includeMetadata: true as const };
|
|
195
|
+
|
|
196
|
+
const topicQuery = extractTopicQuery(queryText);
|
|
197
|
+
|
|
198
|
+
// Dual-query: topic query is the PRIMARY source (better topical relevance);
|
|
199
|
+
// the full query fills remaining slots with unique results only.
|
|
200
|
+
if (topicQuery) {
|
|
201
|
+
const [fullResults, topicResults] = await Promise.all([
|
|
202
|
+
queryWithFallback(ns, { data: queryText, ...queryParams }),
|
|
203
|
+
queryWithFallback(ns, { data: topicQuery, ...queryParams }),
|
|
204
|
+
]);
|
|
205
|
+
|
|
206
|
+
// Topic results first for best topical relevance
|
|
207
|
+
return filterAndMerge([topicResults, fullResults], topK);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// Simple single-query path for short/simple queries
|
|
211
|
+
const results = await queryWithFallback(ns, { data: queryText, ...queryParams });
|
|
212
|
+
return filterAndMerge([results], topK);
|
|
213
|
+
}
|
|
@@ -13,6 +13,10 @@
|
|
|
13
13
|
"default": "https://www.jamdesk.com/docs.json",
|
|
14
14
|
"description": "The URL to the JSON schema file for validation"
|
|
15
15
|
},
|
|
16
|
+
"projectId": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"description": "Jamdesk project identifier. Automatically set by jamdesk deploy."
|
|
19
|
+
},
|
|
16
20
|
"name": {
|
|
17
21
|
"type": "string",
|
|
18
22
|
"minLength": 1,
|
|
@@ -1363,6 +1367,23 @@
|
|
|
1363
1367
|
},
|
|
1364
1368
|
"additionalProperties": false,
|
|
1365
1369
|
"description": "Configure page metadata display settings"
|
|
1370
|
+
},
|
|
1371
|
+
"chat": {
|
|
1372
|
+
"type": "object",
|
|
1373
|
+
"properties": {
|
|
1374
|
+
"enabled": {
|
|
1375
|
+
"type": "boolean",
|
|
1376
|
+
"description": "Enable AI-powered chat assistant (default: true). Set to false to disable.",
|
|
1377
|
+
"default": true
|
|
1378
|
+
},
|
|
1379
|
+
"starterQuestions": {
|
|
1380
|
+
"type": "array",
|
|
1381
|
+
"items": { "type": "string", "minLength": 5, "maxLength": 200 },
|
|
1382
|
+
"maxItems": 4,
|
|
1383
|
+
"description": "Suggested questions shown in chat empty state. Auto-generated by AI during builds when omitted. Set to [] to disable."
|
|
1384
|
+
}
|
|
1385
|
+
},
|
|
1386
|
+
"additionalProperties": false
|
|
1366
1387
|
}
|
|
1367
1388
|
},
|
|
1368
1389
|
"required": [
|
|
@@ -1383,6 +1404,9 @@
|
|
|
1383
1404
|
"$schema": {
|
|
1384
1405
|
"$ref": "#/anyOf/0/properties/$schema"
|
|
1385
1406
|
},
|
|
1407
|
+
"projectId": {
|
|
1408
|
+
"$ref": "#/anyOf/0/properties/projectId"
|
|
1409
|
+
},
|
|
1386
1410
|
"name": {
|
|
1387
1411
|
"$ref": "#/anyOf/0/properties/name"
|
|
1388
1412
|
},
|
|
@@ -1460,6 +1484,9 @@
|
|
|
1460
1484
|
},
|
|
1461
1485
|
"metadata": {
|
|
1462
1486
|
"$ref": "#/anyOf/0/properties/metadata"
|
|
1487
|
+
},
|
|
1488
|
+
"chat": {
|
|
1489
|
+
"$ref": "#/anyOf/0/properties/chat"
|
|
1463
1490
|
}
|
|
1464
1491
|
},
|
|
1465
1492
|
"required": [
|
|
@@ -1480,6 +1507,9 @@
|
|
|
1480
1507
|
"$schema": {
|
|
1481
1508
|
"$ref": "#/anyOf/0/properties/$schema"
|
|
1482
1509
|
},
|
|
1510
|
+
"projectId": {
|
|
1511
|
+
"$ref": "#/anyOf/0/properties/projectId"
|
|
1512
|
+
},
|
|
1483
1513
|
"name": {
|
|
1484
1514
|
"$ref": "#/anyOf/0/properties/name"
|
|
1485
1515
|
},
|
|
@@ -1557,6 +1587,9 @@
|
|
|
1557
1587
|
},
|
|
1558
1588
|
"metadata": {
|
|
1559
1589
|
"$ref": "#/anyOf/0/properties/metadata"
|
|
1590
|
+
},
|
|
1591
|
+
"chat": {
|
|
1592
|
+
"$ref": "#/anyOf/0/properties/chat"
|
|
1560
1593
|
}
|
|
1561
1594
|
},
|
|
1562
1595
|
"required": [
|
|
@@ -379,6 +379,7 @@ async function runDev() {
|
|
|
379
379
|
JAMDESK_PROJECTS_DIR: projectsDir, // Pass the resolved projects directory to child scripts
|
|
380
380
|
HOST_AT_DOCS: hostAtDocs ? 'true' : 'false', // Pass to Next.js config
|
|
381
381
|
NEXT_PUBLIC_BASE_PATH: hostAtDocs ? '/docs' : '', // Pass to client components for asset URLs
|
|
382
|
+
NEXT_PUBLIC_PROJECT_SLUG: effectiveProjectName, // Pass to chat hook for local dev API routing
|
|
382
383
|
};
|
|
383
384
|
|
|
384
385
|
try {
|
|
@@ -451,6 +452,11 @@ async function runDev() {
|
|
|
451
452
|
env,
|
|
452
453
|
});
|
|
453
454
|
|
|
455
|
+
// Note: No file watcher needed for docs.json. In dev mode, getDocsConfig()
|
|
456
|
+
// reads directly from the project source (not public/docs.json) with mtime-based
|
|
457
|
+
// cache invalidation. Edits are picked up automatically on browser refresh.
|
|
458
|
+
// Writing to public/ during dev triggers Turbopack recompilation hangs.
|
|
459
|
+
|
|
454
460
|
// Step 4: Run Next.js dev server
|
|
455
461
|
console.log(`\nš Starting dev server on port ${port}...`);
|
|
456
462
|
// Get first page and OpenAPI config from docs.json (reuse projectDocsJson from step 1)
|
package/vendored/shared/types.ts
CHANGED
|
@@ -12,18 +12,19 @@ export interface BuildParams {
|
|
|
12
12
|
projectId: string;
|
|
13
13
|
buildId: string;
|
|
14
14
|
slug: string;
|
|
15
|
-
repoFullName
|
|
16
|
-
branch
|
|
17
|
-
installationId
|
|
15
|
+
repoFullName?: string; // Optional for CLI builds
|
|
16
|
+
branch?: string; // Optional for CLI builds
|
|
17
|
+
installationId?: number; // Optional for CLI builds
|
|
18
18
|
triggeredBy: string;
|
|
19
|
-
commitSha
|
|
20
|
-
commitMessage
|
|
19
|
+
commitSha?: string; // Optional for CLI builds
|
|
20
|
+
commitMessage?: string; // Optional for CLI builds
|
|
21
21
|
fullRebuild?: boolean; // If true, forces full R2 upload (ignores manifest)
|
|
22
22
|
docsPath?: string | null; // Monorepo: path to docs.json directory (e.g., "jamdesk")
|
|
23
23
|
domain?: string; // Project's custom domain or subdomain (e.g., "docs.acme.com" or "acme.jamdesk.app")
|
|
24
24
|
showBranding?: boolean; // Show "Powered by Jamdesk" in footer (defaults to true)
|
|
25
25
|
hostAtDocs?: boolean; // URL structure: true = /docs/*, false = root /*
|
|
26
26
|
autoMigrate?: boolean; // If true, auto-convert deprecated components during build
|
|
27
|
+
tarballKey?: string; // R2 key for CLI-uploaded tarball
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export interface ErrorDetails {
|
|
@@ -33,6 +33,15 @@ export default {
|
|
|
33
33
|
'theme-code-bg': 'var(--color-code-bg)',
|
|
34
34
|
'theme-code-text': 'var(--color-code-text)',
|
|
35
35
|
},
|
|
36
|
+
keyframes: {
|
|
37
|
+
'fd-fade-in': {
|
|
38
|
+
from: { opacity: '0' },
|
|
39
|
+
to: { opacity: '1' },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
animation: {
|
|
43
|
+
'fd-fade-in': 'fd-fade-in 0.15s ease-in forwards',
|
|
44
|
+
},
|
|
36
45
|
},
|
|
37
46
|
},
|
|
38
47
|
}
|
|
@@ -216,8 +216,8 @@ body[data-theme="jam"]:not(.dark) main {
|
|
|
216
216
|
background-color: transparent !important;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
/* Target wrapper divs that contain the main content */
|
|
220
|
-
body[data-theme="jam"]:not(.dark) > div > div.flex {
|
|
219
|
+
/* Target wrapper divs that contain the main content (exclude chat panel) */
|
|
220
|
+
body[data-theme="jam"]:not(.dark) > div > div.flex:not([role="dialog"]):not(:has([data-chat-panel])) {
|
|
221
221
|
background-color: transparent !important;
|
|
222
222
|
}
|
|
223
223
|
|