@farming-labs/theme 0.1.58 → 0.1.60

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.
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { useCallback, useEffect, useRef, useState } from "react";
4
4
  import { createPortal } from "react-dom";
5
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
5
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
6
6
  import { highlight } from "sugar-high";
7
7
 
8
8
  //#region src/ai-search-dialog.tsx
@@ -635,7 +635,7 @@ function DocsSearchDialog({ open, onOpenChange, api = "/api/docs", suggestedQues
635
635
  };
636
636
  if (!open) return null;
637
637
  const aiName = aiLabel || "AI";
638
- return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
638
+ return createPortal(/* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
639
639
  onClick: () => onOpenChange(false),
640
640
  className: "fd-ai-overlay"
641
641
  }), /* @__PURE__ */ jsxs("div", {
@@ -863,7 +863,7 @@ function FloatingAIChat({ api = "/api/docs", position = "bottom-right", floating
863
863
  const isModal = floatingStyle === "modal";
864
864
  const containerStyles = getContainerStyles(floatingStyle, position);
865
865
  const aiName = aiLabel || "AI";
866
- return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [
866
+ return createPortal(/* @__PURE__ */ jsxs(Fragment$1, { children: [
867
867
  isOpen && isModal && /* @__PURE__ */ jsx("div", {
868
868
  onClick: () => setIsOpen(false),
869
869
  className: "fd-ai-overlay"
@@ -1030,7 +1030,7 @@ function FullModalAIChat({ api, isOpen, setIsOpen, messages, setMessages, aiInpu
1030
1030
  if (canSend) submitQuestion(aiInput);
1031
1031
  }
1032
1032
  };
1033
- return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [isOpen && /* @__PURE__ */ jsxs("div", {
1033
+ return createPortal(/* @__PURE__ */ jsxs(Fragment$1, { children: [isOpen && /* @__PURE__ */ jsxs("div", {
1034
1034
  className: "fd-ai-fm-overlay",
1035
1035
  onClick: (e) => {
1036
1036
  if (e.target === e.currentTarget) setIsOpen(false);
@@ -1186,7 +1186,7 @@ function AIModalDialog({ open, onOpenChange, api = "/api/docs", suggestedQuestio
1186
1186
  }, [open]);
1187
1187
  if (!open) return null;
1188
1188
  const aiName = aiLabel || "AI";
1189
- return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
1189
+ return createPortal(/* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
1190
1190
  onClick: () => onOpenChange(false),
1191
1191
  className: "fd-ai-overlay"
1192
1192
  }), /* @__PURE__ */ jsxs("div", {
@@ -4,7 +4,7 @@ import { useWindowSearchParams } from "./client-location.mjs";
4
4
  import { resolveClientLocale, withLangInUrl } from "./i18n.mjs";
5
5
  import { AIModalDialog, DocsSearchDialog, FloatingAIChat } from "./ai-search-dialog.mjs";
6
6
  import { useEffect, useState } from "react";
7
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/docs-ai-features.tsx
10
10
  /**
@@ -124,7 +124,7 @@ function SidebarIconModeAI({ api, suggestedQuestions, aiLabel, loaderVariant, lo
124
124
  window.removeEventListener("fd-open-ai", onAI);
125
125
  };
126
126
  }, []);
127
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(DocsSearchDialog, {
127
+ return /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(DocsSearchDialog, {
128
128
  open: searchOpen,
129
129
  onOpenChange: setSearchOpen,
130
130
  api,
@@ -4,7 +4,7 @@ import { useWindowSearchParams } from "./client-location.mjs";
4
4
  import { resolveClientLocale, withLangInUrl } from "./i18n.mjs";
5
5
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
6
6
  import { createPortal } from "react-dom";
7
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
8
8
 
9
9
  //#region src/docs-command-search.tsx
10
10
  function cn(...classes) {
@@ -56,7 +56,7 @@ function fuzzyScore(query, text) {
56
56
  };
57
57
  }
58
58
  function HighlightedLabel({ label, indices }) {
59
- if (!indices.length) return /* @__PURE__ */ jsx(Fragment, { children: label });
59
+ if (!indices.length) return /* @__PURE__ */ jsx(Fragment$1, { children: label });
60
60
  const out = [];
61
61
  for (let pos = 0; pos < label.length; pos++) if (indices.includes(pos)) {
62
62
  let run = label[pos];
@@ -71,7 +71,7 @@ function HighlightedLabel({ label, indices }) {
71
71
  }, `m-${pos}`));
72
72
  pos = p - 1;
73
73
  } else out.push(/* @__PURE__ */ jsx("span", { children: label[pos] }, `t-${pos}`));
74
- return /* @__PURE__ */ jsx(Fragment, { children: out });
74
+ return /* @__PURE__ */ jsx(Fragment$1, { children: out });
75
75
  }
76
76
  function SearchIcon() {
77
77
  return /* @__PURE__ */ jsxs("svg", {
@@ -492,7 +492,7 @@ function DocsCommandSearch({ api = "/api/docs", locale }) {
492
492
  }
493
493
  if (!mounted || !open) return null;
494
494
  const allItems = [...recentItems, ...displayItems];
495
- return createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
495
+ return createPortal(/* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("div", {
496
496
  className: "omni-overlay",
497
497
  onClick: () => setOpen(false)
498
498
  }), /* @__PURE__ */ jsxs("div", {
@@ -587,7 +587,7 @@ function createDocsLayout(config, options) {
587
587
  const readingTimeWordsPerMinute = readingTimeOptions.wordsPerMinute ?? 220;
588
588
  const llmsTxtEnabled = resolveBool(config.llmsTxt);
589
589
  const feedbackConfig = resolveFeedbackConfig(config.feedback);
590
- const openDocsProviders = (typeof pageActions?.openDocs === "object" && pageActions.openDocs.providers ? pageActions.openDocs.providers : void 0)?.map((p) => ({
590
+ const openDocsProviders = (pageActions?.openDocs && typeof pageActions.openDocs === "object" && pageActions.openDocs.providers ? pageActions.openDocs.providers : void 0)?.map((p) => ({
591
591
  name: p.name,
592
592
  urlTemplate: p.urlTemplate,
593
593
  iconHtml: p.icon ? serializeIcon(p.icon) : void 0
@@ -4,11 +4,11 @@ import { PageActions } from "./page-actions.mjs";
4
4
  import { useWindowSearchParams } from "./client-location.mjs";
5
5
  import { DocsFeedback } from "./docs-feedback.mjs";
6
6
  import { resolveClientLocale, withLangInUrl } from "./i18n.mjs";
7
- import { Children, cloneElement, isValidElement, useEffect, useState } from "react";
7
+ import { Children, Fragment, cloneElement, isValidElement, useEffect, useState } from "react";
8
8
  import { DocsBody, DocsPage, EditOnGitHub } from "fumadocs-ui/layouts/docs/page";
9
9
  import { createPortal } from "react-dom";
10
10
  import { usePathname, useRouter } from "fumadocs-core/framework";
11
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
11
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
12
12
 
13
13
  //#region src/docs-page-client.tsx
14
14
  /**
@@ -124,6 +124,7 @@ function injectTitleDecorations(node, { description, belowTitle }) {
124
124
  node,
125
125
  inserted: false
126
126
  };
127
+ const insertedExtras = extras.map((extra, index) => /* @__PURE__ */ jsx(Fragment, { children: extra }, `fd-title-decoration-${index}`));
127
128
  function visit(current) {
128
129
  if (current == null || typeof current === "boolean") return current;
129
130
  if (inserted) return current;
@@ -134,7 +135,7 @@ function injectTitleDecorations(node, { description, belowTitle }) {
134
135
  if (!isValidElement(current)) return current;
135
136
  if (typeof current.type === "string" && current.type === "h1") {
136
137
  inserted = true;
137
- return Children.toArray([current, ...extras]);
138
+ return /* @__PURE__ */ jsxs(Fragment, { children: [current, insertedExtras] }, "fd-title-decoration-block");
138
139
  }
139
140
  const childProps = current.props ?? null;
140
141
  if (childProps?.children === void 0) return current;
@@ -159,7 +160,7 @@ function injectTitleDecorations(node, { description, belowTitle }) {
159
160
  }
160
161
  function TitleDecorations({ description, belowTitle }) {
161
162
  if (!description && !belowTitle) return null;
162
- return /* @__PURE__ */ jsx(Fragment, { children: Children.toArray([description, belowTitle].filter(Boolean)) });
163
+ return /* @__PURE__ */ jsx(Fragment$1, { children: Children.toArray([description, belowTitle].filter(Boolean)) });
163
164
  }
164
165
  function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled = true, changelogBasePath, entry = "docs", locale, copyMarkdown = false, openDocs = false, openDocsProviders, pageActionsPosition = "below-title", pageActionsAlignment = "left", githubUrl, contentDir, githubBranch = "main", githubDirectory, editOnGithubUrl, lastModifiedMap, lastModified: lastModifiedProp, readingTimeMap, readingTime: readingTimeProp, readingTimeEnabled = false, lastUpdatedEnabled = true, lastUpdatedPosition = "footer", llmsTxtEnabled = false, descriptionMap, description, feedbackEnabled = false, feedbackQuestion, feedbackPlaceholder, feedbackPositiveLabel, feedbackNegativeLabel, feedbackSubmitLabel, feedbackOnFeedback, children }) {
165
166
  const fdTocStyle = tocStyle === "directional" ? "clerk" : void 0;
@@ -318,6 +319,7 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
318
319
  description: titleDescription,
319
320
  belowTitle: belowTitleBlock
320
321
  }), titlePortalHost) : null;
322
+ const renderedChildren = Children.toArray(decoratedChildren);
321
323
  return /* @__PURE__ */ jsxs(DocsPage, {
322
324
  full: false,
323
325
  toc,
@@ -360,7 +362,7 @@ function DocsPageClient({ tocEnabled, tocStyle = "default", breadcrumbEnabled =
360
362
  children: [
361
363
  /* @__PURE__ */ jsx("div", {
362
364
  style: { flex: 1 },
363
- children: decoratedChildren
365
+ children: renderedChildren
364
366
  }),
365
367
  titleDecorationsPortal,
366
368
  !isChangelogRoute && feedbackEnabled && /* @__PURE__ */ jsx(DocsFeedback, {
package/dist/index.d.mts CHANGED
@@ -11,10 +11,11 @@ import { DocsFeedback, DocsFeedbackProps } from "./docs-feedback.mjs";
11
11
  import { PageActions } from "./page-actions.mjs";
12
12
  import { withLangInUrl } from "./i18n.mjs";
13
13
  import { HoverLink, HoverLinkProps } from "./hover-link.mjs";
14
+ import { Prompt, PromptProps } from "./prompt.mjs";
14
15
  import { Agent } from "./mdx.mjs";
15
16
  import { DocsLayout } from "fumadocs-ui/layouts/docs";
16
17
  import { AIConfig, BreadcrumbConfig, ChangelogConfig, ChangelogFrontmatter, CopyMarkdownConfig, DocsConfig, DocsFeedbackData, DocsFeedbackValue, DocsMetadata, DocsNav, DocsTheme, FeedbackConfig, FontStyle, OGConfig, OpenDocsConfig, OpenDocsProvider, PageActionsConfig, PageFrontmatter, SidebarConfig, ThemeToggleConfig, TypographyConfig, UIConfig, createTheme, deepMerge, defineDocs, extendTheme } from "@farming-labs/docs";
17
18
  import { DocsBody, DocsPage } from "fumadocs-ui/layouts/docs/page";
18
19
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
19
20
  import { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre } from "fumadocs-ui/components/codeblock";
20
- export { type AIConfig, Agent, type BreadcrumbConfig, type ChangelogConfig, type ChangelogFrontmatter, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, CommandGridUIDefaults, ConcreteUIDefaults, type CopyMarkdownConfig, DocsBody, DocsClientHooks, DocsCommandSearch, type DocsConfig, DocsFeedback, type DocsFeedbackData, type DocsFeedbackProps, type DocsFeedbackValue, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FeedbackConfig, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, HardlineUIDefaults, HoverLink, type HoverLinkProps, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, commandGrid, concrete, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, hardline, withLangInUrl };
21
+ export { type AIConfig, Agent, type BreadcrumbConfig, type ChangelogConfig, type ChangelogFrontmatter, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, CommandGridUIDefaults, ConcreteUIDefaults, type CopyMarkdownConfig, DocsBody, DocsClientHooks, DocsCommandSearch, type DocsConfig, DocsFeedback, type DocsFeedbackData, type DocsFeedbackProps, type DocsFeedbackValue, DocsLayout, type DocsMetadata, type DocsNav, DocsPage, DocsPageClient, type DocsTheme, type FeedbackConfig, type FontStyle, DefaultUIDefaults as FumadocsUIDefaults, HardlineUIDefaults, HoverLink, type HoverLinkProps, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, PageActions, type PageActionsConfig, type PageFrontmatter, Pre, Prompt, type PromptProps, RootProvider, type SidebarConfig, Tab, Tabs, type ThemeToggleConfig, type TypographyConfig, type UIConfig, commandGrid, concrete, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, hardline, withLangInUrl };
package/dist/index.mjs CHANGED
@@ -11,6 +11,7 @@ import { ConcreteUIDefaults, concrete } from "./concrete/index.mjs";
11
11
  import { HardlineUIDefaults, hardline } from "./hardline/index.mjs";
12
12
  import { DocsClientHooks } from "./docs-client-hooks.mjs";
13
13
  import { HoverLink } from "./hover-link.mjs";
14
+ import { Prompt } from "./prompt.mjs";
14
15
  import { Agent } from "./mdx.mjs";
15
16
  import { DocsLayout } from "fumadocs-ui/layouts/docs";
16
17
  import { createTheme, deepMerge, defineDocs, extendTheme } from "@farming-labs/docs";
@@ -18,4 +19,4 @@ import { DocsBody, DocsPage } from "fumadocs-ui/layouts/docs/page";
18
19
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
19
20
  import { CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, Pre } from "fumadocs-ui/components/codeblock";
20
21
 
21
- export { Agent, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, CommandGridUIDefaults, ConcreteUIDefaults, DocsBody, DocsClientHooks, DocsCommandSearch, DocsFeedback, DocsLayout, DocsPage, DocsPageClient, DefaultUIDefaults as FumadocsUIDefaults, HardlineUIDefaults, HoverLink, PageActions, Pre, RootProvider, Tab, Tabs, commandGrid, concrete, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, hardline, withLangInUrl };
22
+ export { Agent, CodeBlock, CodeBlockTab, CodeBlockTabs, CodeBlockTabsList, CodeBlockTabsTrigger, CommandGridUIDefaults, ConcreteUIDefaults, DocsBody, DocsClientHooks, DocsCommandSearch, DocsFeedback, DocsLayout, DocsPage, DocsPageClient, DefaultUIDefaults as FumadocsUIDefaults, HardlineUIDefaults, HoverLink, PageActions, Pre, Prompt, RootProvider, Tab, Tabs, commandGrid, concrete, createDocsLayout, createDocsMetadata, createPageMetadata, createTheme, deepMerge, defineDocs, extendTheme, fumadocs, hardline, withLangInUrl };
package/dist/mdx.d.mts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { MDXImg } from "./mdx-img.mjs";
2
2
  import { HoverLink } from "./hover-link.mjs";
3
+ import { Prompt, PromptIconValue, PromptOpenDocsProvider } from "./prompt.mjs";
3
4
  import React from "react";
4
5
  import { CodeBlockCopyData, DocsTheme } from "@farming-labs/docs";
5
6
  import * as react_jsx_runtime0 from "react/jsx-runtime";
@@ -21,6 +22,7 @@ declare const extendedMdxComponents: {
21
22
  table: typeof Table;
22
23
  Agent: typeof Agent;
23
24
  HoverLink: typeof HoverLink;
25
+ Prompt: typeof Prompt;
24
26
  Tab: typeof Tab;
25
27
  Tabs: typeof Tabs;
26
28
  CodeBlockTab: typeof fumadocs_ui_components_codeblock0.CodeBlockTab;
@@ -47,7 +49,11 @@ interface GetMDXComponentsOptions {
47
49
  onCopyClick?: (data: CodeBlockCopyData) => void;
48
50
  /** Theme config used to apply built-in MDX component defaults from `theme.ui.components`. */
49
51
  theme?: DocsTheme;
52
+ /** Shared icon registry from `docs.config.ts[x]`. */
53
+ icons?: Record<string, PromptIconValue>;
54
+ /** Optional site-wide "Open in …" providers used by built-in components such as `Prompt`. */
55
+ openDocsProviders?: PromptOpenDocsProvider[];
50
56
  }
51
57
  declare function getMDXComponents<T extends Record<string, unknown> = Record<string, unknown>>(overrides?: T, options?: GetMDXComponentsOptions): typeof extendedMdxComponents & T;
52
58
  //#endregion
53
- export { Agent, GetMDXComponentsOptions, HoverLink, Tab, Tabs, defaultMdxComponents, extendedMdxComponents, getMDXComponents };
59
+ export { Agent, GetMDXComponentsOptions, HoverLink, Prompt, Tab, Tabs, defaultMdxComponents, extendedMdxComponents, getMDXComponents };
package/dist/mdx.mjs CHANGED
@@ -1,6 +1,8 @@
1
1
  import { MDXImg } from "./mdx-img.mjs";
2
2
  import { createPreWithCopyCallback } from "./code-block-copy-wrapper.mjs";
3
3
  import { HoverLink } from "./hover-link.mjs";
4
+ import { extractPromptText } from "./prompt-text.mjs";
5
+ import { Prompt } from "./prompt.mjs";
4
6
  import React from "react";
5
7
  import { Tab, Tabs } from "fumadocs-ui/components/tabs";
6
8
  import defaultMdxComponents from "fumadocs-ui/mdx";
@@ -36,17 +38,32 @@ const extendedMdxComponents = {
36
38
  table: Table,
37
39
  Agent,
38
40
  HoverLink,
41
+ Prompt,
39
42
  Tab,
40
43
  Tabs
41
44
  };
42
- const mdxComponentDefaults = { HoverLink: {
43
- linkLabel: "Open page",
44
- showIndicator: false,
45
- align: "center",
46
- side: "bottom",
47
- sideOffset: 12,
48
- closeDelay: 90
49
- } };
45
+ const mdxComponentDefaults = {
46
+ HoverLink: {
47
+ linkLabel: "Open page",
48
+ showIndicator: false,
49
+ align: "center",
50
+ side: "bottom",
51
+ sideOffset: 12,
52
+ closeDelay: 90
53
+ },
54
+ Prompt: {
55
+ showTitle: true,
56
+ showDescription: true,
57
+ showPrompt: false,
58
+ actions: ["copy"],
59
+ copyLabel: "Copy prompt",
60
+ copiedLabel: "Copied",
61
+ openLabel: "Open in",
62
+ copyIcon: "copy",
63
+ copiedIcon: "check",
64
+ openIcon: "arrowUpRight"
65
+ }
66
+ };
50
67
  function applyBuiltInComponentDefaults(options) {
51
68
  const themeComponents = options?.theme?.ui?.components;
52
69
  if (!themeComponents) return extendedMdxComponents;
@@ -57,9 +74,10 @@ function applyBuiltInComponentDefaults(options) {
57
74
  const Component = components[name];
58
75
  if (!Component) continue;
59
76
  const builtInDefaults = mdxComponentDefaults[name] ?? {};
77
+ const configuredDefaults = value && typeof value === "object" ? value : {};
60
78
  const componentDefaults = typeof value === "function" ? value(builtInDefaults) : {
61
79
  ...builtInDefaults,
62
- ...value
80
+ ...configuredDefaults
63
81
  };
64
82
  if (!componentDefaults || typeof componentDefaults !== "object") continue;
65
83
  components[name] = function ThemedComponent(props) {
@@ -76,6 +94,19 @@ function getMDXComponents(overrides, options) {
76
94
  ...applyBuiltInComponentDefaults(options),
77
95
  ...overrides
78
96
  };
97
+ if (base.Prompt) {
98
+ const DefaultPrompt = base.Prompt;
99
+ base.Prompt = function PromptWithDocsContext(props) {
100
+ const { children, ...rest } = props;
101
+ return React.createElement(DefaultPrompt, {
102
+ iconRegistry: options?.icons,
103
+ openDocsProviders: options?.openDocsProviders,
104
+ prompt: extractPromptText(children),
105
+ ...rest,
106
+ children
107
+ });
108
+ };
109
+ }
79
110
  if (options?.onCopyClick) {
80
111
  const DefaultPre = base.pre;
81
112
  if (DefaultPre) base.pre = createPreWithCopyCallback(DefaultPre, options.onCopyClick);
@@ -84,4 +115,4 @@ function getMDXComponents(overrides, options) {
84
115
  }
85
116
 
86
117
  //#endregion
87
- export { Agent, HoverLink, Tab, Tabs, defaultMdxComponents, extendedMdxComponents, getMDXComponents };
118
+ export { Agent, HoverLink, Prompt, Tab, Tabs, defaultMdxComponents, extendedMdxComponents, getMDXComponents };
@@ -0,0 +1,23 @@
1
+ import React from "react";
2
+
3
+ //#region src/prompt-text.ts
4
+ function extractPromptText(children) {
5
+ function inner(node) {
6
+ if (node == null || typeof node === "boolean") return "";
7
+ if (typeof node === "string" || typeof node === "number") return String(node);
8
+ if (Array.isArray(node)) return node.map((child) => inner(child)).join("");
9
+ if (React.isValidElement(node)) {
10
+ const childText = inner(node.props.children);
11
+ if (node.type === "br") return "\n";
12
+ if (node.type === "li") return `- ${childText.trim()}\n`;
13
+ if (node.type === "p") return `${childText.trim()}\n\n`;
14
+ if (node.type === "ul" || node.type === "ol") return `${childText.trim()}\n`;
15
+ return childText;
16
+ }
17
+ return "";
18
+ }
19
+ return inner(children).replace(/\r\n/g, "\n").replace(/\n{3,}/g, "\n\n").replace(/[ \t]+\n/g, "\n").trim();
20
+ }
21
+
22
+ //#endregion
23
+ export { extractPromptText };
@@ -0,0 +1,53 @@
1
+ import React from "react";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/prompt.d.ts
5
+ type PromptAction = "copy" | "open";
6
+ type PromptIconValue = React.ReactNode | string;
7
+ interface PromptOpenDocsProvider {
8
+ name: string;
9
+ icon?: PromptIconValue;
10
+ urlTemplate: string;
11
+ promptUrlTemplate?: string;
12
+ }
13
+ interface PromptProps {
14
+ title?: string;
15
+ description?: string;
16
+ prompt?: string;
17
+ icon?: string;
18
+ showTitle?: boolean;
19
+ showDescription?: boolean;
20
+ showPrompt?: boolean;
21
+ actions?: PromptAction[] | string[];
22
+ providers?: string[] | string;
23
+ copyLabel?: string;
24
+ copiedLabel?: string;
25
+ openLabel?: string;
26
+ copyIcon?: string | false;
27
+ copiedIcon?: string | false;
28
+ openIcon?: string | false;
29
+ iconRegistry?: Record<string, PromptIconValue>;
30
+ openDocsProviders?: PromptOpenDocsProvider[];
31
+ }
32
+ declare function Prompt({
33
+ title,
34
+ description,
35
+ prompt,
36
+ icon,
37
+ showTitle,
38
+ showDescription,
39
+ showPrompt,
40
+ actions,
41
+ providers,
42
+ copyLabel,
43
+ copiedLabel,
44
+ openLabel,
45
+ copyIcon,
46
+ copiedIcon,
47
+ openIcon,
48
+ iconRegistry,
49
+ openDocsProviders,
50
+ children
51
+ }: React.PropsWithChildren<PromptProps>): react_jsx_runtime0.JSX.Element;
52
+ //#endregion
53
+ export { Prompt, PromptIconValue, PromptOpenDocsProvider, PromptProps };
@@ -0,0 +1,281 @@
1
+ "use client";
2
+
3
+ import { extractPromptText } from "./prompt-text.mjs";
4
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+
7
+ //#region src/prompt.tsx
8
+ const builtInIcons = {
9
+ copy: /* @__PURE__ */ jsxs("svg", {
10
+ width: "14",
11
+ height: "14",
12
+ viewBox: "0 0 24 24",
13
+ fill: "none",
14
+ stroke: "currentColor",
15
+ strokeWidth: "2",
16
+ strokeLinecap: "round",
17
+ strokeLinejoin: "round",
18
+ children: [/* @__PURE__ */ jsx("rect", {
19
+ x: "9",
20
+ y: "9",
21
+ width: "13",
22
+ height: "13",
23
+ rx: "2",
24
+ ry: "2"
25
+ }), /* @__PURE__ */ jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })]
26
+ }),
27
+ check: /* @__PURE__ */ jsx("svg", {
28
+ width: "14",
29
+ height: "14",
30
+ viewBox: "0 0 24 24",
31
+ fill: "none",
32
+ stroke: "currentColor",
33
+ strokeWidth: "2",
34
+ strokeLinecap: "round",
35
+ strokeLinejoin: "round",
36
+ children: /* @__PURE__ */ jsx("polyline", { points: "20 6 9 17 4 12" })
37
+ }),
38
+ arrowUpRight: /* @__PURE__ */ jsxs("svg", {
39
+ width: "14",
40
+ height: "14",
41
+ viewBox: "0 0 24 24",
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth: "2",
45
+ strokeLinecap: "round",
46
+ strokeLinejoin: "round",
47
+ children: [
48
+ /* @__PURE__ */ jsx("path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" }),
49
+ /* @__PURE__ */ jsx("polyline", { points: "15 3 21 3 21 9" }),
50
+ /* @__PURE__ */ jsx("line", {
51
+ x1: "10",
52
+ y1: "14",
53
+ x2: "21",
54
+ y2: "3"
55
+ })
56
+ ]
57
+ }),
58
+ chevronDown: /* @__PURE__ */ jsx("svg", {
59
+ width: "12",
60
+ height: "12",
61
+ viewBox: "0 0 24 24",
62
+ fill: "none",
63
+ stroke: "currentColor",
64
+ strokeWidth: "2",
65
+ strokeLinecap: "round",
66
+ strokeLinejoin: "round",
67
+ children: /* @__PURE__ */ jsx("polyline", { points: "6 9 12 15 18 9" })
68
+ })
69
+ };
70
+ const defaultPromptProviderTemplates = {
71
+ chatgpt: "https://chatgpt.com/?q={prompt}",
72
+ claude: "https://claude.ai/new?q={prompt}",
73
+ cursor: "https://cursor.com/link/prompt?text={prompt}",
74
+ gemini: "https://gemini.google.com/app?q={prompt}",
75
+ copilot: "https://github.com/copilot?prompt={prompt}"
76
+ };
77
+ function normalizeProviderName(name) {
78
+ return name.trim().toLowerCase();
79
+ }
80
+ function parseStringArray(value) {
81
+ if (Array.isArray(value)) {
82
+ const normalized = value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
83
+ return normalized.length > 0 ? normalized : void 0;
84
+ }
85
+ if (typeof value !== "string") return void 0;
86
+ const trimmed = value.trim();
87
+ if (!trimmed) return void 0;
88
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
89
+ const normalized = trimmed.slice(1, -1).split(",").map((entry) => entry.trim().replace(/^['"]|['"]$/g, "")).filter(Boolean);
90
+ return normalized.length > 0 ? normalized : void 0;
91
+ }
92
+ const normalized = trimmed.split(",").map((entry) => entry.trim()).filter(Boolean);
93
+ return normalized.length > 0 ? normalized : void 0;
94
+ }
95
+ function resolveProviderChoices(availableProviders, preferredNames) {
96
+ const configuredByName = new Map((availableProviders ?? []).map((provider) => [normalizeProviderName(provider.name), provider]));
97
+ const names = preferredNames && preferredNames.length > 0 ? preferredNames : (availableProviders ?? []).map((provider) => provider.name);
98
+ const seen = /* @__PURE__ */ new Set();
99
+ const resolved = [];
100
+ for (const rawName of names) {
101
+ const name = rawName.trim();
102
+ if (!name) continue;
103
+ const normalized = normalizeProviderName(name);
104
+ if (seen.has(normalized)) continue;
105
+ seen.add(normalized);
106
+ const configured = configuredByName.get(normalized);
107
+ const template = configured?.promptUrlTemplate ?? configured?.urlTemplate ?? defaultPromptProviderTemplates[normalized];
108
+ if (!template) continue;
109
+ resolved.push({
110
+ name: configured?.name ?? name,
111
+ icon: configured?.icon,
112
+ urlTemplate: template
113
+ });
114
+ }
115
+ return resolved;
116
+ }
117
+ function resolveActionIcon(name, iconRegistry) {
118
+ if (name === false) return null;
119
+ if (!name) return null;
120
+ const registryMatch = iconRegistry?.[name];
121
+ if (registryMatch) {
122
+ if (typeof registryMatch !== "string") return registryMatch;
123
+ }
124
+ if (name in builtInIcons) return builtInIcons[name];
125
+ return null;
126
+ }
127
+ async function fallbackCopyText(text) {
128
+ if (typeof document === "undefined") return false;
129
+ const textarea = document.createElement("textarea");
130
+ textarea.value = text;
131
+ textarea.setAttribute("readonly", "");
132
+ textarea.style.position = "fixed";
133
+ textarea.style.top = "0";
134
+ textarea.style.left = "0";
135
+ textarea.style.opacity = "0";
136
+ textarea.style.pointerEvents = "none";
137
+ document.body.appendChild(textarea);
138
+ textarea.focus();
139
+ textarea.select();
140
+ let copied = false;
141
+ try {
142
+ copied = document.execCommand("copy");
143
+ } catch {
144
+ copied = false;
145
+ } finally {
146
+ document.body.removeChild(textarea);
147
+ }
148
+ return copied;
149
+ }
150
+ function Prompt({ title, description, prompt, icon, showTitle = true, showDescription = true, showPrompt = false, actions, providers, copyLabel = "Copy prompt", copiedLabel = "Copied", openLabel = "Open in", copyIcon = "copy", copiedIcon = "check", openIcon = "arrowUpRight", iconRegistry, openDocsProviders, children }) {
151
+ const [copied, setCopied] = useState(false);
152
+ const [menuOpen, setMenuOpen] = useState(false);
153
+ const dropdownRef = useRef(null);
154
+ const promptText = useMemo(() => typeof prompt === "string" && prompt.trim() ? prompt.trim() : extractPromptText(children), [prompt, children]);
155
+ const promptIconValue = icon && iconRegistry?.[icon] ? iconRegistry[icon] : void 0;
156
+ const promptIcon = promptIconValue && typeof promptIconValue !== "string" ? promptIconValue : null;
157
+ const resolvedActions = useMemo(() => parseStringArray(actions) ?? ["copy"], [actions]);
158
+ const resolvedProviders = useMemo(() => resolveProviderChoices(openDocsProviders, parseStringArray(providers)), [openDocsProviders, providers]);
159
+ const handleCopy = useCallback(async () => {
160
+ if (!promptText) return;
161
+ try {
162
+ if (typeof navigator !== "undefined" && navigator.clipboard?.writeText) await navigator.clipboard.writeText(promptText);
163
+ else if (!await fallbackCopyText(promptText)) return;
164
+ setCopied(true);
165
+ window.setTimeout(() => setCopied(false), 2e3);
166
+ } catch {
167
+ if (!await fallbackCopyText(promptText)) return;
168
+ setCopied(true);
169
+ window.setTimeout(() => setCopied(false), 2e3);
170
+ }
171
+ }, [promptText]);
172
+ const handleOpen = useCallback((provider) => {
173
+ if (!promptText) return;
174
+ const targetUrl = provider.urlTemplate.replace(/\{prompt\}/g, encodeURIComponent(promptText));
175
+ window.open(targetUrl, "_blank", "noopener,noreferrer");
176
+ setMenuOpen(false);
177
+ }, [promptText]);
178
+ useEffect(() => {
179
+ if (!menuOpen) return;
180
+ function handleOutsideClick(event) {
181
+ if (!dropdownRef.current) return;
182
+ if (!dropdownRef.current.contains(event.target)) setMenuOpen(false);
183
+ }
184
+ document.addEventListener("mousedown", handleOutsideClick);
185
+ return () => document.removeEventListener("mousedown", handleOutsideClick);
186
+ }, [menuOpen]);
187
+ const showCopy = resolvedActions.includes("copy");
188
+ const showOpen = resolvedActions.includes("open") && resolvedProviders.length > 0;
189
+ const singleProvider = showOpen && resolvedProviders.length === 1 ? resolvedProviders[0] : null;
190
+ const visibleTitle = showTitle ? title : void 0;
191
+ const visibleDescription = showDescription ? description : void 0;
192
+ return /* @__PURE__ */ jsxs("div", {
193
+ className: "fd-prompt",
194
+ "data-prompt-card": true,
195
+ children: [
196
+ (promptIcon || visibleTitle || visibleDescription) && /* @__PURE__ */ jsxs("div", {
197
+ className: "fd-prompt-header",
198
+ children: [promptIcon ? /* @__PURE__ */ jsx("span", {
199
+ className: "fd-prompt-icon",
200
+ children: promptIcon
201
+ }) : null, /* @__PURE__ */ jsxs("div", {
202
+ className: "fd-prompt-copy",
203
+ children: [visibleTitle && /* @__PURE__ */ jsx("p", {
204
+ className: "fd-prompt-title",
205
+ children: visibleTitle
206
+ }), visibleDescription && /* @__PURE__ */ jsx("p", {
207
+ className: "fd-prompt-description",
208
+ children: visibleDescription
209
+ })]
210
+ })]
211
+ }),
212
+ showPrompt && promptText && /* @__PURE__ */ jsx("div", {
213
+ className: "fd-prompt-body",
214
+ children: /* @__PURE__ */ jsx("pre", {
215
+ className: "fd-prompt-code",
216
+ children: promptText
217
+ })
218
+ }),
219
+ (showCopy || showOpen) && /* @__PURE__ */ jsxs("div", {
220
+ className: "fd-prompt-actions",
221
+ children: [
222
+ showCopy && /* @__PURE__ */ jsxs("button", {
223
+ type: "button",
224
+ className: "fd-prompt-action-btn",
225
+ "data-copied": copied,
226
+ onClick: handleCopy,
227
+ children: [copied ? resolveActionIcon(copiedIcon, iconRegistry) : resolveActionIcon(copyIcon, iconRegistry), /* @__PURE__ */ jsx("span", { children: copied ? copiedLabel : copyLabel })]
228
+ }),
229
+ singleProvider ? /* @__PURE__ */ jsxs("button", {
230
+ type: "button",
231
+ className: "fd-prompt-action-btn",
232
+ onClick: () => handleOpen(singleProvider),
233
+ children: [resolveActionIcon(openIcon, iconRegistry), /* @__PURE__ */ jsxs("span", { children: [
234
+ openLabel,
235
+ " ",
236
+ singleProvider.name
237
+ ] })]
238
+ }) : null,
239
+ !singleProvider && showOpen ? /* @__PURE__ */ jsxs("div", {
240
+ ref: dropdownRef,
241
+ className: "fd-prompt-dropdown",
242
+ children: [/* @__PURE__ */ jsxs("button", {
243
+ type: "button",
244
+ className: "fd-prompt-action-btn",
245
+ "aria-expanded": menuOpen,
246
+ onClick: () => setMenuOpen((current) => !current),
247
+ children: [
248
+ resolveActionIcon(openIcon, iconRegistry),
249
+ /* @__PURE__ */ jsx("span", { children: openLabel }),
250
+ builtInIcons.chevronDown
251
+ ]
252
+ }), menuOpen && /* @__PURE__ */ jsx("div", {
253
+ className: "fd-prompt-menu",
254
+ role: "menu",
255
+ children: resolvedProviders.map((provider) => /* @__PURE__ */ jsxs("button", {
256
+ type: "button",
257
+ role: "menuitem",
258
+ className: "fd-prompt-menu-item",
259
+ onClick: () => handleOpen(provider),
260
+ children: [provider.icon && typeof provider.icon !== "string" ? /* @__PURE__ */ jsx("span", {
261
+ className: "fd-prompt-menu-icon",
262
+ children: provider.icon
263
+ }) : null, /* @__PURE__ */ jsxs("span", {
264
+ className: "fd-prompt-menu-label",
265
+ children: [
266
+ openLabel,
267
+ " ",
268
+ provider.name
269
+ ]
270
+ })]
271
+ }, provider.name))
272
+ })]
273
+ }) : null
274
+ ]
275
+ })
276
+ ]
277
+ });
278
+ }
279
+
280
+ //#endregion
281
+ export { Prompt };
@@ -238,7 +238,7 @@ function TanstackDocsLayout({ config, tree, locale, description, readingTime, la
238
238
  const llmsTxtEnabled = resolveBool(config.llmsTxt);
239
239
  const feedbackConfig = resolveFeedbackConfig(config.feedback);
240
240
  const staticExport = !!config.staticExport;
241
- const openDocsProviders = (typeof pageActions?.openDocs === "object" && pageActions.openDocs.providers ? pageActions.openDocs.providers : void 0)?.map((provider) => ({
241
+ const openDocsProviders = (pageActions?.openDocs && typeof pageActions.openDocs === "object" && pageActions.openDocs.providers ? pageActions.openDocs.providers : void 0)?.map((provider) => ({
242
242
  name: provider.name,
243
243
  urlTemplate: provider.urlTemplate
244
244
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/theme",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "Theme package for @farming-labs/docs — layout, provider, MDX components, and styles",
5
5
  "keywords": [
6
6
  "docs",
@@ -133,7 +133,7 @@
133
133
  "tsdown": "^0.20.3",
134
134
  "typescript": "^5.9.3",
135
135
  "vitest": "^3.2.4",
136
- "@farming-labs/docs": "0.1.58"
136
+ "@farming-labs/docs": "0.1.60"
137
137
  },
138
138
  "peerDependencies": {
139
139
  "@farming-labs/docs": ">=0.0.1",
package/styles/base.css CHANGED
@@ -422,12 +422,12 @@ figure.shiki:has(figcaption) figcaption {
422
422
  }
423
423
  }
424
424
 
425
- .fd-page-action-menu-item {
425
+ .fd-page-action-menu-item,
426
+ .fd-prompt-menu-item {
426
427
  display: flex;
427
428
  align-items: center;
428
429
  gap: 0.5rem;
429
430
  width: 100%;
430
- padding: 0.2rem 0.625rem;
431
431
  font-size: 0.8125rem;
432
432
  font-weight: 400;
433
433
  color: var(--color-fd-popover-foreground);
@@ -441,9 +441,8 @@ figure.shiki:has(figcaption) figcaption {
441
441
  color 0.1s;
442
442
  }
443
443
 
444
- .fd-page-action-menu-item:hover {
445
- background: var(--color-fd-accent);
446
- color: var(--color-fd-accent-foreground);
444
+ .fd-page-action-menu-item {
445
+ padding: 0.2rem 0.625rem;
447
446
  }
448
447
 
449
448
  .fd-page-action-menu-icon {
@@ -464,6 +463,177 @@ figure.shiki:has(figcaption) figcaption {
464
463
  flex: 1;
465
464
  }
466
465
 
466
+ /* ─── Prompt Cards ─────────────────────────────────────────────────── */
467
+
468
+ .fd-prompt {
469
+ display: flex;
470
+ flex-direction: column;
471
+ gap: 0.875rem;
472
+ margin: 1.25rem 0;
473
+ padding: 1rem;
474
+ border: 1px solid var(--color-fd-border);
475
+ border-radius: 0.75rem;
476
+ background: color-mix(in srgb, var(--color-fd-card) 78%, transparent);
477
+ }
478
+
479
+ .fd-prompt-header {
480
+ display: flex;
481
+ align-items: flex-start;
482
+ gap: 0.75rem;
483
+ }
484
+
485
+ .fd-prompt-icon,
486
+ .fd-prompt-action-icon,
487
+ .fd-prompt-menu-icon {
488
+ display: inline-flex;
489
+ align-items: center;
490
+ justify-content: center;
491
+ flex-shrink: 0;
492
+ }
493
+
494
+ .fd-prompt-icon {
495
+ width: 1.75rem;
496
+ height: 1.75rem;
497
+ border-radius: 0.5rem;
498
+ border: 1px solid color-mix(in srgb, var(--color-fd-border) 80%, transparent);
499
+ background: color-mix(in srgb, var(--color-fd-background) 55%, transparent);
500
+ color: var(--color-fd-foreground);
501
+ }
502
+
503
+ .fd-prompt-icon svg,
504
+ .fd-prompt-action-icon svg,
505
+ .fd-prompt-menu-icon svg {
506
+ width: 1rem;
507
+ height: 1rem;
508
+ }
509
+
510
+ .fd-prompt-copy {
511
+ min-width: 0;
512
+ }
513
+
514
+ .fd-prompt-title {
515
+ margin: 0;
516
+ color: var(--color-fd-foreground);
517
+ font-size: 0.9375rem;
518
+ font-weight: 600;
519
+ line-height: 1.35;
520
+ }
521
+
522
+ .fd-prompt-description {
523
+ margin: 0.25rem 0 0;
524
+ color: var(--color-fd-muted-foreground);
525
+ font-size: 0.875rem;
526
+ line-height: 1.55;
527
+ }
528
+
529
+ .fd-prompt-body {
530
+ margin: 0;
531
+ padding: 0.875rem 1rem;
532
+ border-radius: 0.625rem;
533
+ border: 1px solid color-mix(in srgb, var(--color-fd-border) 82%, transparent);
534
+ background: color-mix(in srgb, var(--color-fd-background) 45%, transparent);
535
+ overflow-x: auto;
536
+ }
537
+
538
+ .fd-prompt-body > pre.fd-prompt-code {
539
+ margin: 0;
540
+ padding: 0 !important;
541
+ border: 0 !important;
542
+ border-radius: 0 !important;
543
+ background: transparent !important;
544
+ box-shadow: none !important;
545
+ color: var(--color-fd-foreground);
546
+ font-size: 0.875rem;
547
+ line-height: 1.65;
548
+ white-space: pre-wrap;
549
+ word-break: break-word;
550
+ font-family: var(
551
+ --fd-font-mono,
552
+ ui-monospace,
553
+ "SF Mono",
554
+ SFMono-Regular,
555
+ Menlo,
556
+ Consolas,
557
+ monospace
558
+ );
559
+ }
560
+
561
+ .fd-prompt-actions {
562
+ display: flex;
563
+ flex-wrap: wrap;
564
+ gap: 0.5rem;
565
+ align-items: center;
566
+ justify-content: flex-end;
567
+ }
568
+
569
+ .fd-prompt-action-btn {
570
+ display: inline-flex;
571
+ align-items: center;
572
+ gap: 0.4375rem;
573
+ min-height: 2rem;
574
+ padding: 0.375rem 0.75rem;
575
+ border: 1px solid var(--color-fd-border);
576
+ border-radius: 0.375rem;
577
+ background: var(--color-fd-secondary);
578
+ color: var(--color-fd-muted-foreground);
579
+ font-size: 0.8125rem;
580
+ font-weight: 500;
581
+ line-height: 1;
582
+ cursor: pointer;
583
+ transition:
584
+ color 0.15s,
585
+ background 0.15s,
586
+ border-color 0.15s;
587
+ }
588
+
589
+ .fd-prompt-action-btn:hover {
590
+ color: var(--color-fd-accent-foreground);
591
+ background: var(--color-fd-accent);
592
+ }
593
+
594
+ .fd-prompt-action-btn[data-copied="true"] {
595
+ color: var(--color-fd-foreground);
596
+ }
597
+
598
+ .fd-prompt-action-icon-copied[hidden] {
599
+ display: none;
600
+ }
601
+
602
+ .fd-prompt-dropdown {
603
+ position: relative;
604
+ }
605
+
606
+ .fd-prompt-menu {
607
+ position: absolute;
608
+ top: calc(100% + 0.375rem);
609
+ right: 0;
610
+ z-index: 50;
611
+ min-width: 220px;
612
+ padding: 0.375rem;
613
+ background: var(--color-fd-popover);
614
+ border: 1px solid var(--color-fd-border);
615
+ border-radius: 0.5rem;
616
+ box-shadow: 0 4px 24px hsl(0 0% 0% / 0.15);
617
+ display: flex;
618
+ flex-direction: column;
619
+ gap: 0.125rem;
620
+ animation: fd-page-actions-fade-in 0.12s ease-out;
621
+ }
622
+
623
+ .fd-prompt-menu-item {
624
+ padding: 0.25rem 0.625rem;
625
+ }
626
+
627
+ .fd-page-action-menu-item:hover,
628
+ .fd-prompt-menu-item:hover {
629
+ background: var(--color-fd-accent);
630
+ color: var(--color-fd-accent-foreground);
631
+ }
632
+
633
+ .fd-prompt-menu-label {
634
+ flex: 1;
635
+ }
636
+
467
637
  /* ─── Docs Feedback ────────────────────────────────────────────────── */
468
638
 
469
639
  .fd-feedback {
@@ -529,6 +529,68 @@ hr {
529
529
  font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
530
530
  }
531
531
 
532
+ /* ─── Prompt (pixel-border overrides) ────────────────────────────── */
533
+
534
+ .fd-prompt {
535
+ border-radius: 0 !important;
536
+ border: 1px solid var(--color-fd-border, #262626) !important;
537
+ background: var(--color-fd-card, var(--color-fd-background)) !important;
538
+ box-shadow: 4px 4px 0 0 var(--color-fd-border, #262626) !important;
539
+ }
540
+
541
+ .fd-prompt-body {
542
+ border-radius: 0 !important;
543
+ border-top: 1px solid var(--color-fd-border, #262626) !important;
544
+ background: var(--color-fd-background, #0c0c0c) !important;
545
+ }
546
+
547
+ .fd-prompt-action-btn {
548
+ border-radius: 0 !important;
549
+ font-size: 0.75rem !important;
550
+ letter-spacing: 0.03em !important;
551
+ text-transform: uppercase !important;
552
+ box-shadow: 2px 2px 0 0 var(--color-fd-border, #262626) !important;
553
+ font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
554
+ background: var(--color-fd-background, #0c0c0c) !important;
555
+ color: var(--color-fd-foreground, inherit) !important;
556
+ border: 1px solid var(--color-fd-border, #262626) !important;
557
+ }
558
+
559
+ .fd-prompt-action-btn:hover {
560
+ background: var(--color-fd-muted, var(--color-fd-accent)) !important;
561
+ color: var(--color-fd-foreground, inherit) !important;
562
+ }
563
+
564
+ .fd-prompt-menu {
565
+ border-radius: 0 !important;
566
+ box-shadow:
567
+ 4px 4px 0 0 var(--color-fd-border, #262626),
568
+ 0 4px 24px hsl(0 0% 0% / 0.5) !important;
569
+ background: var(--color-fd-popover, var(--color-fd-background)) !important;
570
+ border: 2px solid var(--color-fd-border, #262626) !important;
571
+ padding: 0.375rem !important;
572
+ }
573
+
574
+ .fd-prompt-menu-item {
575
+ border-radius: 0 !important;
576
+ font-size: 0.8rem !important;
577
+ letter-spacing: 0.03em !important;
578
+ text-transform: uppercase !important;
579
+ color: var(--color-fd-popover-foreground, var(--color-fd-foreground)) !important;
580
+ font-family: var(--fd-font-mono, var(--font-geist-mono, ui-monospace, monospace)) !important;
581
+ background: transparent !important;
582
+ border-top: 1px solid var(--color-fd-border, #262626) !important;
583
+ }
584
+
585
+ .fd-prompt-menu-item:first-child {
586
+ border-top: none !important;
587
+ }
588
+
589
+ .fd-prompt-menu-item:hover {
590
+ background: var(--color-fd-muted, var(--color-fd-accent)) !important;
591
+ color: var(--color-fd-foreground, inherit) !important;
592
+ }
593
+
532
594
  /* model selector */
533
595
  .fd-ai-model-select-row {
534
596
  display: flex;