blume 2.0.0 → 2.0.1
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.md +1 -1
- package/CHANGELOG.md +28 -0
- package/README.md +2 -2
- package/dist/cli/{chunk-f7t03s3g.js → chunk-27g6wdth.js} +2 -2
- package/dist/cli/{chunk-mnqj32sj.js → chunk-2hn4b8z7.js} +13 -13
- package/dist/cli/chunk-2hn4b8z7.js.map +12 -0
- package/dist/cli/{chunk-by2290sx.js → chunk-5shv93fd.js} +2 -2
- package/dist/cli/{chunk-a9kptbw5.js → chunk-6crbhc3x.js} +3 -3
- package/dist/cli/{chunk-a9kptbw5.js.map → chunk-6crbhc3x.js.map} +1 -1
- package/dist/cli/{chunk-mwt1k8n7.js → chunk-6hsn950k.js} +20 -20
- package/dist/cli/chunk-6hsn950k.js.map +10 -0
- package/dist/cli/{chunk-11j0384y.js → chunk-6vm74dry.js} +13 -13
- package/dist/cli/{chunk-11j0384y.js.map → chunk-6vm74dry.js.map} +3 -3
- package/dist/cli/{chunk-j8mw0za6.js → chunk-79jhk4py.js} +8 -8
- package/dist/cli/{chunk-j8mw0za6.js.map → chunk-79jhk4py.js.map} +2 -2
- package/dist/cli/{chunk-nk3ts2xk.js → chunk-82bbrxdn.js} +2 -2
- package/dist/cli/{chunk-2q1dwty4.js → chunk-ah61y8py.js} +8 -8
- package/dist/cli/{chunk-2q1dwty4.js.map → chunk-ah61y8py.js.map} +4 -4
- package/dist/cli/{chunk-zxccj738.js → chunk-ce574jw2.js} +1 -1
- package/dist/cli/{chunk-y3e45rc8.js → chunk-ch6g3ar0.js} +3 -3
- package/dist/cli/{chunk-beat36xx.js → chunk-dh8cwk36.js} +5 -5
- package/dist/cli/{chunk-beat36xx.js.map → chunk-dh8cwk36.js.map} +2 -2
- package/dist/cli/{chunk-1w8dp3qb.js → chunk-epjnccmv.js} +13 -13
- package/dist/cli/{chunk-ernrthtr.js → chunk-f2z5v128.js} +13 -13
- package/dist/cli/{chunk-zg2gtj10.js → chunk-fs23ddbb.js} +2 -2
- package/dist/cli/{chunk-7ez8ny0t.js → chunk-fxypxtvm.js} +2 -2
- package/dist/cli/{chunk-tzne8qfq.js → chunk-fz5wtpmh.js} +13 -13
- package/dist/cli/{chunk-b5aj94ah.js → chunk-hdpx1tax.js} +4 -4
- package/dist/cli/{chunk-d80hr03s.js → chunk-jwyddg7y.js} +9 -9
- package/dist/cli/{chunk-d80hr03s.js.map → chunk-jwyddg7y.js.map} +2 -2
- package/dist/cli/{chunk-fh5hj5jt.js → chunk-kdp5q7ke.js} +15 -15
- package/dist/cli/{chunk-6k8vp3ta.js → chunk-kpf8rrjc.js} +9 -9
- package/dist/cli/{chunk-6k8vp3ta.js.map → chunk-kpf8rrjc.js.map} +3 -3
- package/dist/cli/{chunk-5a2z0198.js → chunk-m3vmjgmq.js} +9 -9
- package/dist/cli/{chunk-5a2z0198.js.map → chunk-m3vmjgmq.js.map} +2 -2
- package/dist/cli/{chunk-bctazmbk.js → chunk-mb2919y2.js} +4 -4
- package/dist/cli/{chunk-79njf86q.js → chunk-q5163e60.js} +13 -13
- package/dist/cli/{chunk-xaz13gwg.js → chunk-qkqwkpte.js} +196 -208
- package/dist/cli/{chunk-xaz13gwg.js.map → chunk-qkqwkpte.js.map} +48 -48
- package/dist/cli/{chunk-sqn5t4q0.js → chunk-qs4q5p4e.js} +3 -3
- package/dist/cli/{chunk-bw22s759.js → chunk-qwsrynx5.js} +1 -1
- package/dist/cli/{chunk-z01ze5c1.js → chunk-s1p84fyh.js} +15 -15
- package/dist/cli/{chunk-pnnvybbk.js → chunk-s6jhgk0q.js} +5 -5
- package/dist/cli/{chunk-pnnvybbk.js.map → chunk-s6jhgk0q.js.map} +2 -2
- package/dist/cli/{chunk-f2972sbt.js → chunk-vtk4a6dg.js} +1 -1
- package/dist/cli/{chunk-z1f5arsg.js → chunk-wgm7m9qk.js} +21 -21
- package/dist/cli/{chunk-z1f5arsg.js.map → chunk-wgm7m9qk.js.map} +4 -4
- package/dist/cli/{chunk-bnbmcwfb.js → chunk-wm7js3j9.js} +5 -5
- package/dist/cli/{chunk-bnbmcwfb.js.map → chunk-wm7js3j9.js.map} +3 -3
- package/dist/cli/{chunk-d1tadaw7.js → chunk-yt5n7ppj.js} +3 -3
- package/dist/cli/{chunk-pat2zzwc.js → chunk-yw7dm696.js} +1 -1
- package/dist/cli/{chunk-pat2zzwc.js.map → chunk-yw7dm696.js.map} +1 -1
- package/dist/cli/{chunk-88cpgt6h.js → chunk-zxcczpyx.js} +1 -1
- package/dist/cli/{chunk-41za066z.js → chunk-zxh4d9vy.js} +4 -4
- package/dist/cli/index.js +17 -17
- package/dist/types/ai/agent-readability.d.ts +1 -1
- package/dist/types/ai/api/paths.d.ts +1 -1
- package/dist/types/ai/ask-context.d.ts +7 -7
- package/dist/types/ai/ask.d.ts +43 -43
- package/dist/types/ai/index.d.ts +3 -3
- package/dist/types/ai/openapi-components.d.ts +1 -1
- package/dist/types/ai/serializers.d.ts +1 -1
- package/dist/types/ai/visibility.d.ts +1 -1
- package/dist/types/core/config-input.d.ts +17 -17
- package/dist/types/core/config.d.ts +3 -3
- package/dist/types/core/data.d.ts +3 -3
- package/dist/types/core/i18n-ui.d.ts +6 -8
- package/dist/types/core/schema.d.ts +5 -5
- package/dist/types/core/unrecognized-keys.d.ts +1 -1
- package/dist/types/search/documents.d.ts +1 -1
- package/dist/types/search/orama-index.d.ts +1 -1
- package/docs/02-deployment.mdx +4 -4
- package/docs/03-upgrading.mdx +22 -9
- package/docs/04-migrating.mdx +4 -4
- package/docs/08-faq.mdx +3 -3
- package/docs/advanced/custom-pages.mdx +2 -2
- package/docs/advanced/skills.mdx +1 -1
- package/docs/cli/audit.mdx +3 -3
- package/docs/cli/doctor.mdx +3 -3
- package/docs/cli/evals.mdx +7 -7
- package/docs/cli/index.mdx +2 -2
- package/docs/cli/translate.mdx +8 -8
- package/docs/configuration/{ask-ai.mdx → assistant.mdx} +19 -19
- package/docs/configuration/customization.mdx +2 -2
- package/docs/configuration/index.mdx +2 -2
- package/docs/configuration/meta.ts +1 -1
- package/docs/configuration/search.mdx +1 -1
- package/docs/content/i18n.mdx +2 -2
- package/docs/content/islands.mdx +1 -1
- package/docs/discoverability/agent-discovery.mdx +1 -1
- package/docs/discoverability/index.mdx +1 -1
- package/docs/index.mdx +2 -2
- package/package.json +1 -1
- package/skills/blume/SKILL.md +5 -5
- package/skills/blume-migrate/SKILL.md +2 -2
- package/src/ai/agent-readability.ts +4 -4
- package/src/ai/api/paths.ts +1 -1
- package/src/ai/ask-context.ts +7 -7
- package/src/ai/ask-data.ts +2 -2
- package/src/ai/ask.ts +84 -71
- package/src/ai/cors.ts +3 -3
- package/src/ai/index.ts +16 -16
- package/src/ai/openapi-components.ts +1 -1
- package/src/ai/serializers.ts +1 -1
- package/src/ai/visibility.ts +1 -1
- package/src/astro/generate.ts +19 -18
- package/src/astro/module-types.ts +1 -1
- package/src/astro/runtime-deps.ts +6 -6
- package/src/astro/templates.ts +26 -26
- package/src/blume-modules.d.ts +2 -2
- package/src/cli/commands/audit.ts +1 -1
- package/src/cli/commands/doctor.ts +7 -5
- package/src/cli/commands/eval.ts +3 -3
- package/src/cli/commands/migrate.ts +2 -2
- package/src/cli/commands/translate.ts +3 -3
- package/src/cli/commands/upgrade.ts +2 -2
- package/src/cli/required-secrets.ts +3 -3
- package/src/components/copy-feedback.ts +1 -1
- package/src/components/islands/{AskAI.astro → Assistant.astro} +10 -10
- package/src/components/islands/{ask-ai.tsx → assistant.tsx} +23 -23
- package/src/components/islands/hooks.ts +14 -12
- package/src/components/layout/Header.astro +10 -10
- package/src/components/layout/PageLayout.astro +6 -6
- package/src/components/layout/Pagination.astro +7 -7
- package/src/components/layout/ReferenceLayout.astro +1 -1
- package/src/components/layout/RootLayout.astro +6 -6
- package/src/components/layout/Search.astro +13 -13
- package/src/components/layout/analytics-client.ts +1 -1
- package/src/components/layout/drawer-inert.ts +1 -1
- package/src/components/openapi/description.ts +2 -2
- package/src/core/code-fences.ts +1 -1
- package/src/core/config-input.ts +19 -19
- package/src/core/config.ts +3 -3
- package/src/core/data.ts +3 -3
- package/src/core/i18n-ui.ts +35 -9
- package/src/core/request-body.ts +1 -1
- package/src/core/schema.ts +25 -20
- package/src/core/server-features.ts +2 -2
- package/src/core/ui-packs/ar.ts +4 -5
- package/src/core/ui-packs/bg.ts +4 -5
- package/src/core/ui-packs/bn.ts +4 -5
- package/src/core/ui-packs/ca.ts +4 -5
- package/src/core/ui-packs/cs.ts +4 -5
- package/src/core/ui-packs/da.ts +4 -5
- package/src/core/ui-packs/de.ts +4 -5
- package/src/core/ui-packs/el.ts +4 -5
- package/src/core/ui-packs/es.ts +4 -5
- package/src/core/ui-packs/fa.ts +4 -5
- package/src/core/ui-packs/fi.ts +4 -5
- package/src/core/ui-packs/fr.ts +4 -5
- package/src/core/ui-packs/he.ts +4 -5
- package/src/core/ui-packs/hi.ts +4 -5
- package/src/core/ui-packs/hr.ts +4 -5
- package/src/core/ui-packs/hu.ts +4 -5
- package/src/core/ui-packs/id.ts +4 -5
- package/src/core/ui-packs/it.ts +4 -5
- package/src/core/ui-packs/ja.ts +4 -5
- package/src/core/ui-packs/ko.ts +4 -5
- package/src/core/ui-packs/nl.ts +4 -5
- package/src/core/ui-packs/no.ts +4 -5
- package/src/core/ui-packs/pl.ts +4 -5
- package/src/core/ui-packs/pt-br.ts +4 -5
- package/src/core/ui-packs/pt.ts +4 -5
- package/src/core/ui-packs/ro.ts +4 -5
- package/src/core/ui-packs/ru.ts +4 -5
- package/src/core/ui-packs/sk.ts +4 -5
- package/src/core/ui-packs/sr.ts +4 -5
- package/src/core/ui-packs/sv.ts +4 -5
- package/src/core/ui-packs/th.ts +4 -5
- package/src/core/ui-packs/tr.ts +4 -5
- package/src/core/ui-packs/uk.ts +4 -5
- package/src/core/ui-packs/vi.ts +4 -5
- package/src/core/ui-packs/zh-tw.ts +4 -5
- package/src/core/ui-packs/zh.ts +4 -5
- package/src/core/unrecognized-keys.ts +1 -1
- package/src/registry/eject.ts +19 -18
- package/src/search/documents.ts +2 -2
- package/src/search/orama-index.ts +1 -1
- package/src/translate/report.ts +1 -1
- package/src/upgrade/upgrade.ts +1 -1
- package/dist/cli/chunk-mnqj32sj.js.map +0 -12
- package/dist/cli/chunk-mwt1k8n7.js.map +0 -10
- /package/dist/cli/{chunk-f7t03s3g.js.map → chunk-27g6wdth.js.map} +0 -0
- /package/dist/cli/{chunk-by2290sx.js.map → chunk-5shv93fd.js.map} +0 -0
- /package/dist/cli/{chunk-nk3ts2xk.js.map → chunk-82bbrxdn.js.map} +0 -0
- /package/dist/cli/{chunk-zxccj738.js.map → chunk-ce574jw2.js.map} +0 -0
- /package/dist/cli/{chunk-y3e45rc8.js.map → chunk-ch6g3ar0.js.map} +0 -0
- /package/dist/cli/{chunk-1w8dp3qb.js.map → chunk-epjnccmv.js.map} +0 -0
- /package/dist/cli/{chunk-ernrthtr.js.map → chunk-f2z5v128.js.map} +0 -0
- /package/dist/cli/{chunk-zg2gtj10.js.map → chunk-fs23ddbb.js.map} +0 -0
- /package/dist/cli/{chunk-7ez8ny0t.js.map → chunk-fxypxtvm.js.map} +0 -0
- /package/dist/cli/{chunk-tzne8qfq.js.map → chunk-fz5wtpmh.js.map} +0 -0
- /package/dist/cli/{chunk-b5aj94ah.js.map → chunk-hdpx1tax.js.map} +0 -0
- /package/dist/cli/{chunk-fh5hj5jt.js.map → chunk-kdp5q7ke.js.map} +0 -0
- /package/dist/cli/{chunk-bctazmbk.js.map → chunk-mb2919y2.js.map} +0 -0
- /package/dist/cli/{chunk-79njf86q.js.map → chunk-q5163e60.js.map} +0 -0
- /package/dist/cli/{chunk-sqn5t4q0.js.map → chunk-qs4q5p4e.js.map} +0 -0
- /package/dist/cli/{chunk-bw22s759.js.map → chunk-qwsrynx5.js.map} +0 -0
- /package/dist/cli/{chunk-z01ze5c1.js.map → chunk-s1p84fyh.js.map} +0 -0
- /package/dist/cli/{chunk-f2972sbt.js.map → chunk-vtk4a6dg.js.map} +0 -0
- /package/dist/cli/{chunk-d1tadaw7.js.map → chunk-yt5n7ppj.js.map} +0 -0
- /package/dist/cli/{chunk-88cpgt6h.js.map → chunk-zxcczpyx.js.map} +0 -0
- /package/dist/cli/{chunk-41za066z.js.map → chunk-zxh4d9vy.js.map} +0 -0
package/src/ai/cors.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CORS for the generated
|
|
2
|
+
* CORS for the assistant's generated route (`ai.assistant.cors`).
|
|
3
3
|
*
|
|
4
4
|
* A browser only lets a page on another origin read a response that names
|
|
5
5
|
* that origin, and a JSON `POST` preflights first. `preflightResponse` answers
|
|
@@ -8,12 +8,12 @@
|
|
|
8
8
|
* rather than stamping each `return`, keeps the next return site added to the
|
|
9
9
|
* handler from shipping an opaque failure for that one status.
|
|
10
10
|
*
|
|
11
|
-
* `allowed` is the `ai.
|
|
11
|
+
* `allowed` is the `ai.assistant.cors` list: origins already reduced to their
|
|
12
12
|
* `scheme://host[:port]` form by the config schema, or the single entry `"*"`
|
|
13
13
|
* to admit every origin.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
/** The `ai.
|
|
16
|
+
/** The `ai.assistant.cors` entry that admits every origin. */
|
|
17
17
|
export const ANY_ORIGIN = "*";
|
|
18
18
|
|
|
19
19
|
/** The headers a response carries for a cross-origin caller. */
|
package/src/ai/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Assistant provider adapters for `blume.config.ts`:
|
|
3
3
|
*
|
|
4
4
|
* ```ts
|
|
5
5
|
* import { defineConfig } from "blume";
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*
|
|
8
8
|
* export default defineConfig({
|
|
9
9
|
* ai: {
|
|
10
|
-
*
|
|
10
|
+
* assistant: {
|
|
11
11
|
* enabled: true,
|
|
12
12
|
* provider: openrouter({ model: "anthropic/claude-sonnet-4-5" }),
|
|
13
13
|
* },
|
|
@@ -27,19 +27,19 @@ export {
|
|
|
27
27
|
openrouter,
|
|
28
28
|
} from "./ask.ts";
|
|
29
29
|
export type {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
AssistantAdapter,
|
|
31
|
+
AssistantAdapterOptions,
|
|
32
|
+
AssistantGatewayAdapter,
|
|
33
|
+
AssistantGatewayOptions,
|
|
34
|
+
AssistantInkeepAdapter,
|
|
35
|
+
AssistantInkeepOptions,
|
|
36
|
+
AssistantLlmGatewayAdapter,
|
|
37
|
+
AssistantLlmGatewayOptions,
|
|
38
|
+
AssistantOpenAICompatibleAdapter,
|
|
39
|
+
AssistantOpenAICompatibleOptions,
|
|
40
|
+
AssistantOpenRouterAdapter,
|
|
41
|
+
AssistantOpenRouterOptions,
|
|
42
|
+
AssistantProviderOptions,
|
|
43
|
+
AssistantReasoning,
|
|
44
44
|
} from "./ask.ts";
|
|
45
45
|
export type { AdapterDescriptor } from "../core/adapter.ts";
|
|
@@ -44,7 +44,7 @@ const listItem = (
|
|
|
44
44
|
* the body plus one of these components, deliberately: the structured UI is the
|
|
45
45
|
* component's job, and the prose stays Markdown so it indexes. That split is
|
|
46
46
|
* right for the rendered page and lossy everywhere else — `<route>.md`,
|
|
47
|
-
* llms-full.txt, MCP `get_page` and the
|
|
47
|
+
* llms-full.txt, MCP `get_page` and the assistant corpus all downlevel components
|
|
48
48
|
* to Markdown, and these three had no serializer, so an operation page reached
|
|
49
49
|
* an agent as its description followed by a bare tag. On a site whose reference
|
|
50
50
|
* is most of the corpus, that is most of the corpus: measured on one 449-page
|
package/src/ai/serializers.ts
CHANGED
|
@@ -6,7 +6,7 @@ import { openapiComponentSerializers } from "./openapi-components.ts";
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Every serializer a project brings to a downlevel pass, layered once for all
|
|
9
|
-
* the agent surfaces (`<route>.md`, llms-full.txt, the search and
|
|
9
|
+
* the agent surfaces (`<route>.md`, llms-full.txt, the search and assistant
|
|
10
10
|
* corpora): the built-in families that read project data — examples, then the
|
|
11
11
|
* API reference — under the user's `agents.markdownComponents`, which is spread
|
|
12
12
|
* last so an entry of the same name still wins. One layering, so a new family
|
package/src/ai/visibility.ts
CHANGED
|
@@ -67,7 +67,7 @@ export const applyAudienceVisibility = (
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Resolve `<Visibility>` blocks for agent-facing Markdown (llms-full.txt, the
|
|
70
|
-
* `.md`/`.mdx` mirrors, MCP tools,
|
|
70
|
+
* `.md`/`.mdx` mirrors, MCP tools, assistant grounding): `for="web"` content is
|
|
71
71
|
* removed and `for="agents"` content is unwrapped.
|
|
72
72
|
*/
|
|
73
73
|
export const applyAgentVisibility = (markdown: string): string =>
|
package/src/astro/generate.ts
CHANGED
|
@@ -636,7 +636,7 @@ const writeIfChanged = async (
|
|
|
636
636
|
|
|
637
637
|
/**
|
|
638
638
|
* Delete generated files under `srcDir` that this pass didn't (re)write. The
|
|
639
|
-
* generator emits many files conditionally — an
|
|
639
|
+
* generator emits many files conditionally — an assistant endpoint, OG images, a
|
|
640
640
|
* search index, RSS feeds, reference pages, the MCP server — so toggling a
|
|
641
641
|
* feature off would otherwise leave a stale file behind, and a leftover
|
|
642
642
|
* server-rendered endpoint breaks the static build. `writeIfChanged` only ever
|
|
@@ -1114,10 +1114,10 @@ export const buildRuntimeData = (project: BlumeProject): string => {
|
|
|
1114
1114
|
config: {
|
|
1115
1115
|
analytics: config.analytics,
|
|
1116
1116
|
appleIcon: resolveAppleIcon(project),
|
|
1117
|
-
|
|
1117
|
+
assistant: config.ai.assistant?.enabled
|
|
1118
1118
|
? {
|
|
1119
|
-
endpoint: config.ai.
|
|
1120
|
-
suggestions: config.ai.
|
|
1119
|
+
endpoint: config.ai.assistant.endpoint ?? null,
|
|
1120
|
+
suggestions: config.ai.assistant.suggestions,
|
|
1121
1121
|
}
|
|
1122
1122
|
: null,
|
|
1123
1123
|
banner: resolveBanner(config),
|
|
@@ -1580,9 +1580,9 @@ const proxyAllowlistWarnings = (
|
|
|
1580
1580
|
};
|
|
1581
1581
|
|
|
1582
1582
|
/**
|
|
1583
|
-
* Write the
|
|
1583
|
+
* Write the assistant endpoint and, unless the backend runs its own retrieval
|
|
1584
1584
|
* (Inkeep), the grounding snapshot the endpoint queries at request time. A no-op
|
|
1585
|
-
* when
|
|
1585
|
+
* when the assistant is disabled.
|
|
1586
1586
|
*/
|
|
1587
1587
|
const writeAskFiles = async (
|
|
1588
1588
|
project: BlumeProject,
|
|
@@ -1590,20 +1590,20 @@ const writeAskFiles = async (
|
|
|
1590
1590
|
write: (path: string, content: string) => Promise<boolean>,
|
|
1591
1591
|
modules: RuntimeModules
|
|
1592
1592
|
): Promise<void> => {
|
|
1593
|
-
const {
|
|
1594
|
-
if (!(
|
|
1593
|
+
const { assistant } = project.config.ai;
|
|
1594
|
+
if (!(assistant?.enabled && !assistant.endpoint)) {
|
|
1595
1595
|
return;
|
|
1596
1596
|
}
|
|
1597
|
-
const backend = resolveAskBackend(
|
|
1597
|
+
const backend = resolveAskBackend(assistant.provider);
|
|
1598
1598
|
if (backend.grounded) {
|
|
1599
1599
|
modules.set("blume:ask-data", JSON.stringify(await buildAskData(project)));
|
|
1600
1600
|
}
|
|
1601
1601
|
await write(
|
|
1602
1602
|
join(srcDir, "pages", "api", "ask.ts"),
|
|
1603
1603
|
askEndpointTemplate(backend, {
|
|
1604
|
-
cors:
|
|
1605
|
-
instructions:
|
|
1606
|
-
retrieval:
|
|
1604
|
+
cors: assistant.cors,
|
|
1605
|
+
instructions: assistant.instructions,
|
|
1606
|
+
retrieval: assistant.retrieval,
|
|
1607
1607
|
})
|
|
1608
1608
|
);
|
|
1609
1609
|
};
|
|
@@ -1645,7 +1645,7 @@ export const diagnosticWarning = (diagnostic: Diagnostic): string =>
|
|
|
1645
1645
|
|
|
1646
1646
|
/**
|
|
1647
1647
|
* Missing-dependency preflight: the search provider's SDK, content source
|
|
1648
|
-
* SDKs, the
|
|
1648
|
+
* SDKs, the assistant backend's provider SDK, the deployment adapter's package,
|
|
1649
1649
|
* and — since React ships with Blume while Vue/Svelte don't — any island
|
|
1650
1650
|
* framework's Astro integration. A build fails here, before anything is
|
|
1651
1651
|
* written, with the install command for the project's package manager —
|
|
@@ -1845,7 +1845,7 @@ export const generateRuntime = async (
|
|
|
1845
1845
|
const homeLinkHeader =
|
|
1846
1846
|
buildHomeLinkHeader(config, contentRoutes) ?? undefined;
|
|
1847
1847
|
|
|
1848
|
-
const
|
|
1848
|
+
const assistantEnabled = config.ai.assistant?.enabled ?? false;
|
|
1849
1849
|
const exportPdf = config.export.pdf;
|
|
1850
1850
|
const exportEpub = config.export.epub;
|
|
1851
1851
|
// Every route's source Markdown: published as `blume:raw-markdown` below,
|
|
@@ -1897,7 +1897,7 @@ export const generateRuntime = async (
|
|
|
1897
1897
|
project.examples = exampleMarkdownLookup(exampleDiscovery.examples);
|
|
1898
1898
|
|
|
1899
1899
|
// Each island/example framework enables its Astro renderer. React also
|
|
1900
|
-
// switches on for any project `.tsx`/`.jsx` and for
|
|
1900
|
+
// switches on for any project `.tsx`/`.jsx` and for the assistant; Vue/Svelte are
|
|
1901
1901
|
// island/example-driven. `.astro` examples need no renderer. Component
|
|
1902
1902
|
// overrides referencing a framework component enable its renderer too.
|
|
1903
1903
|
const frameworks = new Set<string>([
|
|
@@ -1906,7 +1906,8 @@ export const generateRuntime = async (
|
|
|
1906
1906
|
...slotPlan.frameworks,
|
|
1907
1907
|
]);
|
|
1908
1908
|
const dependencyWarnings = await dependencyPreflight(project, frameworks);
|
|
1909
|
-
const needsReact =
|
|
1909
|
+
const needsReact =
|
|
1910
|
+
detectedReact || assistantEnabled || frameworks.has("react");
|
|
1910
1911
|
const needsVue = frameworks.has("vue");
|
|
1911
1912
|
const needsSvelte = frameworks.has("svelte");
|
|
1912
1913
|
|
|
@@ -2025,7 +2026,7 @@ export const generateRuntime = async (
|
|
|
2025
2026
|
// The header's Ask trigger, behind the `blume:ask` alias. Always written
|
|
2026
2027
|
// (even when Ask is off, as a component that renders nothing) so the alias
|
|
2027
2028
|
// resolves — the same contract as `blume:search-client`.
|
|
2028
|
-
write(askPath, askComponentTemplate(
|
|
2029
|
+
write(askPath, askComponentTemplate(assistantEnabled)),
|
|
2029
2030
|
write(join(srcDir, "generated", "components.ts"), slotPlan.module),
|
|
2030
2031
|
write(
|
|
2031
2032
|
join(srcDir, "generated", "examples.ts"),
|
|
@@ -2288,7 +2289,7 @@ export const generateRuntime = async (
|
|
|
2288
2289
|
writeStagedContent(out, staged),
|
|
2289
2290
|
]);
|
|
2290
2291
|
|
|
2291
|
-
// Remove anything under `.blume/src` this pass didn't write — e.g. an
|
|
2292
|
+
// Remove anything under `.blume/src` this pass didn't write — e.g. an assistant
|
|
2292
2293
|
// endpoint left behind after the feature was switched off.
|
|
2293
2294
|
await pruneOrphans(srcDir, written);
|
|
2294
2295
|
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
export const runtimeModuleDeclarations = (
|
|
15
15
|
examplesModule: string
|
|
16
16
|
): string => `declare module "blume:ask" {
|
|
17
|
-
const Ask: typeof import("blume/components/islands/
|
|
17
|
+
const Ask: typeof import("blume/components/islands/Assistant.astro").default;
|
|
18
18
|
export default Ask;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -85,18 +85,18 @@ export const sourceAdapterDependencies = (
|
|
|
85
85
|
);
|
|
86
86
|
|
|
87
87
|
/**
|
|
88
|
-
* The
|
|
88
|
+
* The assistant adapter's provider SDK, when missing — only `gateway` needs
|
|
89
89
|
* nothing beyond the core `ai` package Blume ships. An external `endpoint`
|
|
90
90
|
* means no generated route, so no SDK is imported.
|
|
91
91
|
*/
|
|
92
|
-
export const
|
|
93
|
-
ask: ResolvedConfig["ai"]["
|
|
92
|
+
export const assistantProviderDependencies = (
|
|
93
|
+
ask: ResolvedConfig["ai"]["assistant"],
|
|
94
94
|
root: string,
|
|
95
95
|
pkgDir: string = packageRoot()
|
|
96
96
|
): MissingDependency[] =>
|
|
97
97
|
ask?.enabled && !ask.endpoint
|
|
98
98
|
? unresolved(
|
|
99
|
-
`
|
|
99
|
+
`Assistant provider "${ask.provider.kind}"`,
|
|
100
100
|
ask.provider.runtimeDeps,
|
|
101
101
|
root,
|
|
102
102
|
pkgDir
|
|
@@ -152,7 +152,7 @@ export const islandFrameworkDependencies = (
|
|
|
152
152
|
/**
|
|
153
153
|
* Every package the resolved config's adapters (and the given island
|
|
154
154
|
* frameworks) import that isn't installed: the search SDK, content source
|
|
155
|
-
* SDKs, the
|
|
155
|
+
* SDKs, the assistant provider SDK, the deployment adapter, and island
|
|
156
156
|
* integrations. The adapters' `runtimeDeps` are the one place that knows.
|
|
157
157
|
*/
|
|
158
158
|
export const missingRuntimeDependencies = (
|
|
@@ -163,7 +163,7 @@ export const missingRuntimeDependencies = (
|
|
|
163
163
|
): MissingDependency[] => [
|
|
164
164
|
...searchProviderDependencies(config.search.provider, root, pkgDir),
|
|
165
165
|
...sourceAdapterDependencies(config.content.sources, root, pkgDir),
|
|
166
|
-
...
|
|
166
|
+
...assistantProviderDependencies(config.ai.assistant, root, pkgDir),
|
|
167
167
|
...deploymentAdapterDependencies(config.deployment, root, pkgDir),
|
|
168
168
|
...islandFrameworkDependencies(frameworks, root),
|
|
169
169
|
];
|
package/src/astro/templates.ts
CHANGED
|
@@ -196,10 +196,10 @@ export const runtimeDependencies = (options: {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
}
|
|
199
|
-
//
|
|
199
|
+
// The assistant's provider SDK, as its adapter declares it (the gateway needs
|
|
200
200
|
// nothing beyond core `ai`, so it declares none).
|
|
201
|
-
if (config.ai.
|
|
202
|
-
deps.push(...config.ai.
|
|
201
|
+
if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
|
|
202
|
+
deps.push(...config.ai.assistant.provider.runtimeDeps);
|
|
203
203
|
}
|
|
204
204
|
// The deployment adapter's `@astrojs/*` package, for a server build; the
|
|
205
205
|
// descriptor declares it (and nothing for a static build).
|
|
@@ -727,7 +727,7 @@ export const astroConfigTemplate = (options: {
|
|
|
727
727
|
hasFonts: fontEntries.length > 0,
|
|
728
728
|
});
|
|
729
729
|
|
|
730
|
-
// Framework renderers are only wired in when an island (or
|
|
730
|
+
// Framework renderers are only wired in when an island (or the assistant, for React)
|
|
731
731
|
// needs them. The core theme is Astro-first and ships no client JS.
|
|
732
732
|
const reactImport = needsReact ? `import react from "@astrojs/react";\n` : "";
|
|
733
733
|
const vueImport = needsVue ? `import vue from "@astrojs/vue";\n` : "";
|
|
@@ -1064,17 +1064,17 @@ export const collections = { docs${options.staged ? ", staged" : ""} };
|
|
|
1064
1064
|
const ASK_FALLBACK_PROMPT =
|
|
1065
1065
|
"You are a helpful documentation assistant. Answer using the project's documentation.";
|
|
1066
1066
|
|
|
1067
|
-
/** The `ai.
|
|
1067
|
+
/** The `ai.assistant` values the generated endpoint has to carry with it. */
|
|
1068
1068
|
export interface AskEndpointOptions {
|
|
1069
|
-
/** `ai.
|
|
1069
|
+
/** `ai.assistant.cors` — origins allowed to call the route from another site. */
|
|
1070
1070
|
cors?: string[];
|
|
1071
|
-
/** `ai.
|
|
1071
|
+
/** `ai.assistant.instructions` — extra system-prompt text. */
|
|
1072
1072
|
instructions?: string;
|
|
1073
|
-
/** `ai.
|
|
1073
|
+
/** `ai.assistant.retrieval` — how much documentation each question carries. */
|
|
1074
1074
|
retrieval?: AskRetrievalOptions;
|
|
1075
1075
|
}
|
|
1076
1076
|
|
|
1077
|
-
/** The pieces `askEndpointTemplate` splices in for `ai.
|
|
1077
|
+
/** The pieces `askEndpointTemplate` splices in for `ai.assistant.cors`. */
|
|
1078
1078
|
interface AskCorsTemplate {
|
|
1079
1079
|
/** The route's closing token: `});` when the POST is wrapped, `};` otherwise. */
|
|
1080
1080
|
close: string;
|
|
@@ -1087,7 +1087,7 @@ interface AskCorsTemplate {
|
|
|
1087
1087
|
}
|
|
1088
1088
|
|
|
1089
1089
|
/**
|
|
1090
|
-
* `ai.
|
|
1090
|
+
* `ai.assistant.cors`: a browser only lets another origin read the stream when the
|
|
1091
1091
|
* response names that origin, and a JSON POST preflights first, so the route
|
|
1092
1092
|
* answers `OPTIONS` and wraps the `POST` in `withCors`, which stamps a listed
|
|
1093
1093
|
* origin on every response — errors included, so a cross-origin caller can
|
|
@@ -1114,14 +1114,14 @@ export const OPTIONS: APIRoute = ({ request }) =>
|
|
|
1114
1114
|
: { close: "};", imports: [], open: "async ({ request }) => {", setup: "" };
|
|
1115
1115
|
|
|
1116
1116
|
/**
|
|
1117
|
-
* Largest request body the
|
|
1117
|
+
* Largest request body the assistant route reads: 64 KB, well above the
|
|
1118
1118
|
* 24,000-character message budget it validates next, so a real conversation
|
|
1119
1119
|
* never meets it.
|
|
1120
1120
|
*/
|
|
1121
1121
|
const ASK_BODY_LIMIT_BYTES = 65_536;
|
|
1122
1122
|
|
|
1123
1123
|
/**
|
|
1124
|
-
* Generate the
|
|
1124
|
+
* Generate the assistant server endpoint (`.blume/src/pages/api/ask.ts`).
|
|
1125
1125
|
*
|
|
1126
1126
|
* The provider-specific pieces — imports, the provider factory call, the model
|
|
1127
1127
|
* expression, the credential guard, and the adapter's reasoning mapping and
|
|
@@ -1130,11 +1130,11 @@ const ASK_BODY_LIMIT_BYTES = 65_536;
|
|
|
1130
1130
|
* `blume.config.ts`. `backend.grounded` decides whether answers are grounded
|
|
1131
1131
|
* in the retrieved docs (every adapter but Inkeep, which retrieves itself).
|
|
1132
1132
|
*
|
|
1133
|
-
* `options.instructions` (the `ai.
|
|
1133
|
+
* `options.instructions` (the `ai.assistant.instructions` config) is appended to the
|
|
1134
1134
|
* built-in prompt on every path: the grounded prompt via `createAskContext`,
|
|
1135
|
-
* and the plain fallback here. `options.retrieval` (the `ai.
|
|
1135
|
+
* and the plain fallback here. `options.retrieval` (the `ai.assistant.retrieval`
|
|
1136
1136
|
* config) is forwarded to `createAskContext` on the grounded path, where it
|
|
1137
|
-
* sizes retrieval. `options.cors` (the `ai.
|
|
1137
|
+
* sizes retrieval. `options.cors` (the `ai.assistant.cors` config) adds a preflight
|
|
1138
1138
|
* handler and wraps the `POST` so every response names a listed origin. All
|
|
1139
1139
|
* three travel in one options object so a new call site can't silently drop
|
|
1140
1140
|
* one of them.
|
|
@@ -1232,7 +1232,7 @@ export const askEndpointTemplate = (
|
|
|
1232
1232
|
// Provider errors surface mid-stream, after the 200 is committed; this is
|
|
1233
1233
|
// the only place they can be observed server-side.
|
|
1234
1234
|
const onError = ` onError({ error }) {
|
|
1235
|
-
console.error("
|
|
1235
|
+
console.error("Assistant provider error:", error);
|
|
1236
1236
|
},`;
|
|
1237
1237
|
// The `streamText` argument list, built once so the grounded and plain
|
|
1238
1238
|
// paths can't drift: they differ only in where the instructions come from.
|
|
@@ -1281,9 +1281,9 @@ ${handler}
|
|
|
1281
1281
|
* Generate `.blume/src/generated/Ask.astro` — the component behind the
|
|
1282
1282
|
* `blume:ask` alias that the shared header renders in place of a per-page slot.
|
|
1283
1283
|
*
|
|
1284
|
-
* The header can't import the
|
|
1284
|
+
* The header can't import the assistant island directly: it's a React component, so
|
|
1285
1285
|
* the import alone would drag the JSX renderer into the module graph of every
|
|
1286
|
-
* project — including the ones that never enable
|
|
1286
|
+
* project — including the ones that never enable the assistant and therefore have no
|
|
1287
1287
|
* React integration wired into their generated Astro config (see `needsReact`).
|
|
1288
1288
|
* Routing the import through a generated component keeps that dependency behind
|
|
1289
1289
|
* the config switch: enabled projects get the island, disabled ones get a
|
|
@@ -1293,25 +1293,25 @@ ${handler}
|
|
|
1293
1293
|
* state suggestions are read straight from the data snapshot, which is why no
|
|
1294
1294
|
* page has to pass them.
|
|
1295
1295
|
*/
|
|
1296
|
-
export const askComponentTemplate = (
|
|
1297
|
-
|
|
1296
|
+
export const askComponentTemplate = (assistantEnabled: boolean): string =>
|
|
1297
|
+
assistantEnabled
|
|
1298
1298
|
? `---
|
|
1299
1299
|
// Generated by Blume. Do not edit.
|
|
1300
|
-
import
|
|
1300
|
+
import Assistant from "blume/components/islands/Assistant.astro";
|
|
1301
1301
|
import data from "blume:data";
|
|
1302
1302
|
|
|
1303
1303
|
const { strings } = Astro.props;
|
|
1304
1304
|
---
|
|
1305
1305
|
|
|
1306
|
-
<
|
|
1307
|
-
endpoint={data.config.
|
|
1308
|
-
strings={strings ?? data.ui.
|
|
1309
|
-
suggestions={data.config.
|
|
1306
|
+
<Assistant
|
|
1307
|
+
endpoint={data.config.assistant?.endpoint ?? undefined}
|
|
1308
|
+
strings={strings ?? data.ui.assistant}
|
|
1309
|
+
suggestions={data.config.assistant?.suggestions ?? []}
|
|
1310
1310
|
/>
|
|
1311
1311
|
`
|
|
1312
1312
|
: `---
|
|
1313
1313
|
// Generated by Blume. Do not edit.
|
|
1314
|
-
//
|
|
1314
|
+
// The assistant is off (\`ai.assistant.enabled\`), so the header's Ask trigger renders nothing.
|
|
1315
1315
|
// Deliberately imports no React island, keeping the JSX renderer out of projects
|
|
1316
1316
|
// that don't need it.
|
|
1317
1317
|
---
|
package/src/blume-modules.d.ts
CHANGED
|
@@ -18,9 +18,9 @@ declare module "blume:search-client" {
|
|
|
18
18
|
declare module "blume:ask" {
|
|
19
19
|
/** The generated Ask trigger (see `askComponentTemplate`); empty when Ask is off. */
|
|
20
20
|
// Mirrors the generated `.blume/src/env.d.ts` declaration: the trigger's type
|
|
21
|
-
// is the
|
|
21
|
+
// is the Assistant island the enabled template re-exports.
|
|
22
22
|
// oxlint-disable-next-line typescript/consistent-type-imports
|
|
23
|
-
const Ask: typeof import("./components/islands/
|
|
23
|
+
const Ask: typeof import("./components/islands/Assistant.astro").default;
|
|
24
24
|
export default Ask;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -154,7 +154,7 @@ export const auditCommand = defineCommand({
|
|
|
154
154
|
(kind) => args[kind]
|
|
155
155
|
);
|
|
156
156
|
if (agents.length > 1) {
|
|
157
|
-
logger.error("Pass at most one of --
|
|
157
|
+
logger.error("Pass at most one of --codex or --claude.");
|
|
158
158
|
process.exit(1);
|
|
159
159
|
}
|
|
160
160
|
const [agent] = agents;
|
|
@@ -153,12 +153,14 @@ export const doctorCommand = defineCommand({
|
|
|
153
153
|
logger.info(
|
|
154
154
|
`Sources: ${config.content.sources.map((source) => source.kind).join(", ")}`
|
|
155
155
|
);
|
|
156
|
-
const {
|
|
157
|
-
let
|
|
158
|
-
if (
|
|
159
|
-
|
|
156
|
+
const { assistant } = config.ai;
|
|
157
|
+
let assistantSummary = "off";
|
|
158
|
+
if (assistant?.enabled) {
|
|
159
|
+
assistantSummary = assistant.endpoint
|
|
160
|
+
? "external endpoint"
|
|
161
|
+
: assistant.provider.kind;
|
|
160
162
|
}
|
|
161
|
-
logger.info(`
|
|
163
|
+
logger.info(`Assistant: ${assistantSummary}`);
|
|
162
164
|
}
|
|
163
165
|
} catch (error) {
|
|
164
166
|
if (error instanceof BlumeError) {
|
package/src/cli/commands/eval.ts
CHANGED
|
@@ -82,7 +82,7 @@ interface EvalFlags {
|
|
|
82
82
|
/** Validate the flag surface, exiting with a message on the first offense. */
|
|
83
83
|
const parseFlags = (args: EvalFlags) => {
|
|
84
84
|
if (!isAgentKind(args.agent)) {
|
|
85
|
-
logger.error(`Invalid --agent "${args.agent}" (use
|
|
85
|
+
logger.error(`Invalid --agent "${args.agent}" (use codex | claude).`);
|
|
86
86
|
process.exit(1);
|
|
87
87
|
}
|
|
88
88
|
if (args.action !== undefined && args.action !== "init") {
|
|
@@ -152,8 +152,8 @@ export const evalCommand = defineCommand({
|
|
|
152
152
|
type: "positional",
|
|
153
153
|
},
|
|
154
154
|
agent: {
|
|
155
|
-
default: "
|
|
156
|
-
description: "Agent CLI that reads and grades the docs:
|
|
155
|
+
default: "codex",
|
|
156
|
+
description: "Agent CLI that reads and grades the docs: codex | claude.",
|
|
157
157
|
type: "string",
|
|
158
158
|
},
|
|
159
159
|
file: {
|
|
@@ -53,7 +53,7 @@ export const migrateCommand = defineCommand({
|
|
|
53
53
|
(kind) => args[kind]
|
|
54
54
|
);
|
|
55
55
|
if (agents.length > 1) {
|
|
56
|
-
logger.error("Pass at most one of --
|
|
56
|
+
logger.error("Pass at most one of --codex or --claude.");
|
|
57
57
|
process.exit(1);
|
|
58
58
|
}
|
|
59
59
|
const [agent] = agents;
|
|
@@ -88,7 +88,7 @@ export const migrateCommand = defineCommand({
|
|
|
88
88
|
// plus the install line that puts the skill where agents look for it.
|
|
89
89
|
if (!agent) {
|
|
90
90
|
process.stderr.write(
|
|
91
|
-
` blume migrate runs the migration with a coding agent: rerun with --
|
|
91
|
+
` blume migrate runs the migration with a coding agent: rerun with --codex or --claude.\n Using another agent? Point it at ${join(skillDir, "SKILL.md")},\n or install the skill where your agent looks for skills: npx skills add haydenbleasel/blume --skill blume-migrate\n`
|
|
92
92
|
);
|
|
93
93
|
return;
|
|
94
94
|
}
|
|
@@ -63,18 +63,18 @@ const parseFlags = (args: TranslateFlags): ParsedTranslateFlags => {
|
|
|
63
63
|
(kind) => args[kind]
|
|
64
64
|
);
|
|
65
65
|
if (agents.length > 1) {
|
|
66
|
-
logger.error("Pass exactly one of --
|
|
66
|
+
logger.error("Pass exactly one of --codex or --claude.");
|
|
67
67
|
process.exit(1);
|
|
68
68
|
}
|
|
69
69
|
if (args.check && agents.length > 0) {
|
|
70
70
|
logger.error(
|
|
71
|
-
"--check is read-only and never runs an agent; drop --claude
|
|
71
|
+
"--check is read-only and never runs an agent; drop --codex/--claude."
|
|
72
72
|
);
|
|
73
73
|
process.exit(1);
|
|
74
74
|
}
|
|
75
75
|
if (!args.check && agents.length === 0) {
|
|
76
76
|
logger.error(
|
|
77
|
-
"Pass --
|
|
77
|
+
"Pass --codex or --claude to choose the agent CLI that translates."
|
|
78
78
|
);
|
|
79
79
|
process.exit(1);
|
|
80
80
|
}
|
|
@@ -56,7 +56,7 @@ export const upgradeCommand = defineCommand({
|
|
|
56
56
|
(kind) => args[kind]
|
|
57
57
|
);
|
|
58
58
|
if (agents.length > 1) {
|
|
59
|
-
logger.error("Pass at most one of --
|
|
59
|
+
logger.error("Pass at most one of --codex or --claude.");
|
|
60
60
|
process.exit(1);
|
|
61
61
|
}
|
|
62
62
|
const [agent] = agents;
|
|
@@ -109,7 +109,7 @@ export const upgradeCommand = defineCommand({
|
|
|
109
109
|
|
|
110
110
|
if (!agent) {
|
|
111
111
|
process.stderr.write(
|
|
112
|
-
` Each change is covered in the upgrade guide: ${UPGRADE_GUIDE_URL}\n Rerun with --
|
|
112
|
+
` Each change is covered in the upgrade guide: ${UPGRADE_GUIDE_URL}\n Rerun with --codex or --claude to hand them to a coding agent.\n`
|
|
113
113
|
);
|
|
114
114
|
process.exit(1);
|
|
115
115
|
}
|
|
@@ -24,12 +24,12 @@ export const checkRequiredSecrets = (config: ResolvedConfig): Diagnostic[] => {
|
|
|
24
24
|
});
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
-
if (config.ai.
|
|
27
|
+
if (config.ai.assistant?.enabled && !config.ai.assistant.endpoint) {
|
|
28
28
|
// The adapter descriptor names the env vars its route reads.
|
|
29
|
-
const { provider } = config.ai.
|
|
29
|
+
const { provider } = config.ai.assistant;
|
|
30
30
|
const backend = resolveAskBackend(provider);
|
|
31
31
|
for (const env of provider.requiredSecrets) {
|
|
32
|
-
requireSecret(`
|
|
32
|
+
requireSecret(`Assistant (${backend.label})`, env, backend.secretNote);
|
|
33
33
|
}
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Shared clipboard + "Copied" feedback used by every copy affordance (code
|
|
3
|
-
* blocks, page actions, color swatches, prompts, API panels,
|
|
3
|
+
* blocks, page actions, color swatches, prompts, API panels, the assistant). One
|
|
4
4
|
* implementation owns the invariants each site used to hand-roll:
|
|
5
5
|
*
|
|
6
6
|
* - the clipboard write is guarded and a confirmation must never lie: a
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { UIStrings } from "../../core/i18n-ui.ts";
|
|
3
3
|
import { resolveIcon } from "../../theme/icons.ts";
|
|
4
|
-
import
|
|
4
|
+
import Assistant from "./assistant.tsx";
|
|
5
5
|
|
|
6
6
|
interface Suggestion {
|
|
7
7
|
icon?: string;
|
|
@@ -10,7 +10,7 @@ interface Suggestion {
|
|
|
10
10
|
|
|
11
11
|
interface Props {
|
|
12
12
|
endpoint?: string;
|
|
13
|
-
strings?: UIStrings["
|
|
13
|
+
strings?: UIStrings["assistant"];
|
|
14
14
|
suggestions?: Suggestion[];
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -48,19 +48,19 @@ const icons = {
|
|
|
48
48
|
the draft input, and the open panel — across client-router navigations
|
|
49
49
|
instead of remounting it empty on every page. The island re-portals its
|
|
50
50
|
panel and re-stamps the body push attribute itself on astro:after-swap
|
|
51
|
-
(see
|
|
52
|
-
<
|
|
51
|
+
(see assistant.tsx), since the swap replaces <body> wholesale. */}
|
|
52
|
+
<Assistant
|
|
53
53
|
client:load
|
|
54
54
|
endpoint={endpoint}
|
|
55
55
|
icons={icons}
|
|
56
56
|
strings={strings}
|
|
57
57
|
suggestions={items}
|
|
58
|
-
transition:persist="blume-
|
|
58
|
+
transition:persist="blume-assistant"
|
|
59
59
|
/>
|
|
60
60
|
|
|
61
61
|
<style is:global>
|
|
62
62
|
:root {
|
|
63
|
-
--blume-
|
|
63
|
+
--blume-assistant-width: min(30rem, 100vw);
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
/* Desktop docks the panel and shrinks the page to fit, like vercel.com/docs.
|
|
@@ -70,8 +70,8 @@ const icons = {
|
|
|
70
70
|
transition: padding-inline-end 0.2s ease-out;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
body[data-blume-
|
|
74
|
-
padding-inline-end: var(--blume-
|
|
73
|
+
body[data-blume-assistant="open"] {
|
|
74
|
+
padding-inline-end: var(--blume-assistant-width);
|
|
75
75
|
}
|
|
76
76
|
}
|
|
77
77
|
|
|
@@ -81,11 +81,11 @@ const icons = {
|
|
|
81
81
|
changelog index) is single-column, and forcing the sidebar tracks onto it
|
|
82
82
|
would squeeze its only item — <main> — into the 17.5rem track. */
|
|
83
83
|
@media (min-width: 1280px) {
|
|
84
|
-
body[data-blume-
|
|
84
|
+
body[data-blume-assistant="open"] [data-blume-toc] {
|
|
85
85
|
display: none;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
body[data-blume-
|
|
88
|
+
body[data-blume-assistant="open"] [data-blume-doc-grid]:has([data-blume-toc]) {
|
|
89
89
|
grid-template-columns: 17.5rem minmax(0, 1fr);
|
|
90
90
|
}
|
|
91
91
|
}
|