research-agent-ui 0.1.293 → 0.1.294

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.
Files changed (54) hide show
  1. package/README.md +42 -1
  2. package/dist/ArticleExtractPanel-25tRTnpl.cjs +2 -0
  3. package/dist/ArticleExtractPanel-D_y16YHG.js +389 -0
  4. package/dist/ChatConversationThread-BIJ8aNug.js +1428 -0
  5. package/dist/ChatConversationThread-BoNH0wvs.cjs +17 -0
  6. package/dist/DownloadsDialog-CCPj0hA8.cjs +1 -0
  7. package/dist/{DownloadsDialog-bTEDe2DI.js → DownloadsDialog-DvS23Dh-.js} +8 -9
  8. package/dist/api/handlers/autocomplete.d.ts +6 -9
  9. package/dist/api/handlers/openai-transcription.d.ts +53 -0
  10. package/dist/api/handlers/transcript.d.ts +33 -0
  11. package/dist/api/index.d.ts +1 -0
  12. package/dist/api.cjs +10 -10
  13. package/dist/api.mjs +161 -208
  14. package/dist/app/index.d.ts +1 -1
  15. package/dist/app/useChunkErrorReload.d.ts +13 -2
  16. package/dist/components/ArticleReader/ArticleReaderMount.d.ts +5 -0
  17. package/dist/components/ArticleReader/ExtractPanelContext.d.ts +17 -1
  18. package/dist/components/ChatConversation/MessageBubble/AssistantMessageActions.d.ts +2 -1
  19. package/dist/components/ChatConversation/background-art.d.ts +3 -1
  20. package/dist/components/ChatConversation/background-cache.d.ts +41 -0
  21. package/dist/components/Footer.d.ts +5 -4
  22. package/dist/components/Footer.stories.d.ts +3 -3
  23. package/dist/config.cjs +1 -1
  24. package/dist/config.d.ts +9 -0
  25. package/dist/config.mjs +4 -3
  26. package/dist/dialog-7GDz0sWx.cjs +1 -0
  27. package/dist/dialog-Cufn2u50.js +80 -0
  28. package/dist/hooks/voice/useKokoroTTS.d.ts +2 -0
  29. package/dist/hooks/voice/useTextToVoice.d.ts +2 -1
  30. package/dist/index.cjs +1 -1
  31. package/dist/index.d.ts +5 -1
  32. package/dist/index.mjs +4 -2
  33. package/dist/lib/file-sources.d.ts +8 -0
  34. package/dist/lib/google-drive-source.d.ts +100 -0
  35. package/dist/lib/kokoro.d.ts +14 -2
  36. package/dist/lib/qwksearch-connect.d.ts +79 -0
  37. package/dist/settings/index.d.ts +15 -0
  38. package/dist/src-GPr1ceY8.js +6165 -0
  39. package/dist/src-ebmQu_if.cjs +34 -0
  40. package/dist/tooltip-Bl3IdX-I.js +179 -0
  41. package/dist/tooltip-DmUO6ZUz.cjs +1 -0
  42. package/dist/types/fileSource.d.ts +9 -0
  43. package/dist/workspace/QwkSearchWorkspaceApp.d.ts +9 -2
  44. package/dist/workspace/ResearchWorkspaceView.d.ts +18 -4
  45. package/dist/workspace.cjs +1 -1
  46. package/dist/workspace.d.ts +1 -1
  47. package/dist/workspace.mjs +234 -91
  48. package/package.json +54 -51
  49. package/src/app/useChunkErrorReload.ts +92 -0
  50. package/src/settings/index.ts +23 -0
  51. package/src/settings/search.json +19 -1
  52. package/dist/DownloadsDialog-Ca6aRRcP.cjs +0 -1
  53. package/dist/src-HL9JjPc-.js +0 -8218
  54. package/dist/src-r4BsCIB4.cjs +0 -51
@@ -1,17 +1,33 @@
1
1
  import React, { ReactNode } from 'react';
2
+ /** How the reader should behave once the page is read. */
3
+ export interface OpenPanelOptions {
4
+ /**
5
+ * Write questions about the page as soon as its text is in, when it has
6
+ * none cached — so opening it lands on something to ask, not a blank prompt.
7
+ */
8
+ suggestQuestions?: boolean;
9
+ }
2
10
  interface ExtractPanelContextType {
3
11
  isOpen: boolean;
4
12
  url: string;
5
13
  searchText: string;
14
+ /** Set by the last `openPanel` call; see {@link OpenPanelOptions.suggestQuestions}. */
15
+ suggestQuestions: boolean;
6
16
  panelWidth: number;
7
17
  setIsOpen: (isOpen: boolean) => void;
8
18
  setUrl: (url: string) => void;
9
19
  setSearchText: (searchText: string) => void;
10
20
  setPanelWidth: (width: number) => void;
11
- openPanel: (url: string, searchText?: string) => void;
21
+ openPanel: (url: string, searchText?: string, options?: OpenPanelOptions) => void;
12
22
  closePanel: () => void;
13
23
  }
14
24
  export declare const useExtractPanel: () => ExtractPanelContextType;
25
+ /**
26
+ * Like {@link useExtractPanel}, but `undefined` outside an
27
+ * `ExtractPanelProvider` instead of throwing — for components (the homepage
28
+ * widgets) that offer the reader when a host provides one and work without it.
29
+ */
30
+ export declare const useOptionalExtractPanel: () => ExtractPanelContextType;
15
31
  export declare const ExtractPanelProvider: React.FC<{
16
32
  children: ReactNode;
17
33
  }>;
@@ -3,6 +3,7 @@ import { Section } from '../../../hooks/useChat';
3
3
  interface AssistantMessageActionsProps {
4
4
  section: Section;
5
5
  speechStatus: string;
6
+ isLoadingTTS?: boolean;
6
7
  start: () => void;
7
8
  stop: () => void;
8
9
  handleOpenInLLM: (platform: string) => void;
@@ -11,5 +12,5 @@ interface AssistantMessageActionsProps {
11
12
  * Component for message actions related to the assistant's response.
12
13
  * Includes rewrite, copy, text-to-speech, export, and external LLM links.
13
14
  */
14
- declare const AssistantMessageActions: ({ section, speechStatus, start, stop, handleOpenInLLM, }: AssistantMessageActionsProps) => React.JSX.Element;
15
+ declare const AssistantMessageActions: ({ section, speechStatus, isLoadingTTS, start, stop, handleOpenInLLM, }: AssistantMessageActionsProps) => React.JSX.Element;
15
16
  export default AssistantMessageActions;
@@ -1,9 +1,11 @@
1
1
  /**
2
2
  * @fileoverview Helper functions providing randomized background artwork URLs and time-of-day greeting strings for the chat homepage.
3
3
  */
4
+ /** Every background artwork, as the full Imgur URL. */
5
+ export declare const BACKGROUND_ARTWORKS: readonly string[];
4
6
  /**
5
7
  * Gets a random artistic background about graph theory
6
- * and collective consciousness, 50+ AI-gen artworks found online
8
+ * and collective consciousness
7
9
  * @returns {string} Imgur URL of random artistic background
8
10
  */
9
11
  export declare function getBackgroundArtwork(): string;
@@ -0,0 +1,41 @@
1
+ /** The Cache Storage bucket the artwork lives in. Bump to drop old entries. */
2
+ export declare const BACKGROUND_CACHE_NAME = "qwksearch-background-art-v1";
3
+ /** The `localStorage` key holding the URLs in the cache, oldest first. */
4
+ export declare const BACKGROUND_CACHE_LIST_KEY = "backgroundArtCache";
5
+ /**
6
+ * How many pieces are kept. Once full, the homepage rotates through these and
7
+ * stops downloading, since some of the pieces are multi-megabyte video.
8
+ */
9
+ export declare const MAX_CACHED_BACKGROUNDS = 10;
10
+ /** A piece larger than this is still shown, but not stored. */
11
+ export declare const MAX_CACHED_BACKGROUND_BYTES: number;
12
+ /**
13
+ * The cached URLs, oldest first. Anything that is not (or is no longer) one of
14
+ * the artworks is dropped, so a stale or hand-edited entry can never be loaded.
15
+ */
16
+ export declare function readCachedBackgroundList(): string[];
17
+ /**
18
+ * Records `url` as cached (most recent last) and returns the URLs that fell off
19
+ * the end of the list, which the caller deletes from Cache Storage.
20
+ */
21
+ export declare function rememberCachedBackground(url: string): string[];
22
+ /** Drops `url` from the list, e.g. when its cache entry has gone missing. */
23
+ export declare function forgetCachedBackground(url: string): void;
24
+ /** A random cached piece other than `exclude`, or `null` if there is none. */
25
+ export declare function pickCachedBackground(exclude?: string | null): string | null;
26
+ /** A random piece that is not cached yet, or `null` once every one is. */
27
+ export declare function pickUncachedBackground(): string | null;
28
+ /**
29
+ * Reads a cached piece back as an object URL the page can display, without
30
+ * touching the network. Returns `null` if it is not actually in the cache, in
31
+ * which case it is also dropped from the list.
32
+ */
33
+ export declare function loadCachedBackground(url: string): Promise<string | null>;
34
+ /**
35
+ * Downloads a piece, stores it and records it in the list, evicting the oldest
36
+ * past {@link MAX_CACHED_BACKGROUNDS}. Returns what the page should display:
37
+ * an object URL of the download, or the plain URL where it cannot be cached
38
+ * (no Cache Storage, or the fetch was refused), so the element loads it from
39
+ * the network as it always did. Returns `null` for a failed download.
40
+ */
41
+ export declare function cacheBackground(url: string): Promise<string | null>;
@@ -14,10 +14,11 @@ interface FooterProps {
14
14
  /**
15
15
  * Footer component that displays a list of links with optional icons.
16
16
  *
17
- * On desktop the links render as a bar pinned to the bottom-center of the screen.
18
- * On mobile the bar is collapsed into a small info icon in the bottom corner that,
19
- * when tapped, reveals the same links in a popover — keeping the chat input clear
20
- * of the footer and the OS app dock.
17
+ * The links render as a single compact line at every width, in normal flow,
18
+ * so the parent decides where it sits (the homepage puts it directly under
19
+ * the weather widget). Below the `sm` breakpoint the icons
20
+ * are dropped and the labels shrink so all of the links fit on one line on a
21
+ * phone.
21
22
  *
22
23
  * @param listFooterLinks - Array of footer links with their properties
23
24
  * @param optionShowIcons - Whether to show icons next to links (default: true)
@@ -1,9 +1,9 @@
1
1
  import type { Meta, StoryObj } from '@storybook/react-vite';
2
2
  import Footer from './Footer';
3
3
  /**
4
- * `Footer` renders a compact bar of links (with optional Lucide icons) pinned
5
- * to the bottom of the screen. On small screens it collapses into an info icon
6
- * that reveals the links in a popover. `icon` values are Lucide icon names.
4
+ * `Footer` renders a compact bar of links (with optional Lucide icons) in the
5
+ * normal flow, on a single line at every width; below `sm` the
6
+ * icons are hidden so the labels still fit. `icon` values are Lucide icon names.
7
7
  */
8
8
  declare const meta: Meta<typeof Footer>;
9
9
  export default meta;
package/dist/config.cjs CHANGED
@@ -1 +1 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={appName:`QwkSearch`,defaultSummarizePrompt:`Summarize in bullet points and bold topics`,maxArticleLength:1500,downloadChromeUrl:`https://chromewebstore.google.com/detail/tab-manager-ai/manhemnhmipdhdpabojcplebckhckeko`,downloadWindowsStoreId:`9PCGF9GNK460`,footerLinks:[],googleApiKey:``,googleAppId:``,getAutoMediaSearch:()=>!0,appIconUrl:`/apple-touch-icon.png`,trendingNewsApiUrl:`/api/news/trending`,trendingNewsSettingsUrl:`/api/news/settings`};function t(t){Object.assign(e,t)}exports.configureResearchAgentUI=t,exports.researchAgentUIConfig=e;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e={appName:`QwkSearch`,defaultSummarizePrompt:`Summarize in bullet points and bold topics`,maxArticleLength:1500,downloadChromeUrl:`https://chromewebstore.google.com/detail/tab-manager-ai/manhemnhmipdhdpabojcplebckhckeko`,downloadWindowsStoreId:`9PCGF9GNK460`,footerLinks:[],googleApiKey:``,googleAppId:``,getAutoMediaSearch:()=>!0,appIconUrl:`/apple-touch-icon.png`,trendingNewsApiUrl:`/api/news/trending`,trendingNewsSettingsUrl:`/api/news/settings`,educationPlaylistsApiUrl:`/api/learn`,educationPlaylistsPageUrl:`/learn`};function t(t){Object.assign(e,t)}exports.configureResearchAgentUI=t,exports.researchAgentUIConfig=e;
package/dist/config.d.ts CHANGED
@@ -79,6 +79,15 @@ export interface ResearchAgentUIConfig {
79
79
  * the local settings alone.
80
80
  */
81
81
  trendingNewsSettingsUrl: string;
82
+ /**
83
+ * The education-playlists planner endpoint (`education-playlists/server`
84
+ * mounted by the host). Defaults to this app's `/api/learn`. Set it to an
85
+ * empty string on a host without that route: the homepage widget then plans
86
+ * locally from its bundled catalog, without AI.
87
+ */
88
+ educationPlaylistsApiUrl: string;
89
+ /** Full-page education playlists view the homepage card links to. Empty hides the link. */
90
+ educationPlaylistsPageUrl: string;
82
91
  /**
83
92
  * Requests that the settings UI be opened. Lets the consuming app render
84
93
  * settings in a modal (e.g. on large desktop screens) instead of navigating
package/dist/config.mjs CHANGED
@@ -1,4 +1,4 @@
1
- //#region src/config.ts
1
+ // src/config.ts
2
2
  var e = {
3
3
  appName: "QwkSearch",
4
4
  defaultSummarizePrompt: "Summarize in bullet points and bold topics",
@@ -11,10 +11,11 @@ var e = {
11
11
  getAutoMediaSearch: () => !0,
12
12
  appIconUrl: "/apple-touch-icon.png",
13
13
  trendingNewsApiUrl: "/api/news/trending",
14
- trendingNewsSettingsUrl: "/api/news/settings"
14
+ trendingNewsSettingsUrl: "/api/news/settings",
15
+ educationPlaylistsApiUrl: "/api/learn",
16
+ educationPlaylistsPageUrl: "/learn"
15
17
  };
16
18
  function t(t) {
17
19
  Object.assign(e, t);
18
20
  }
19
- //#endregion
20
21
  export { t as configureResearchAgentUI, e as researchAgentUIConfig };
@@ -0,0 +1 @@
1
+ const e=require("./rolldown-runtime-BocRIvOZ.cjs");let t=require("react");t=e.t(t,1);let n=require("react/jsx-runtime"),r=require("lucide-react"),i=require("clsx"),a=require("tailwind-merge"),o=require("@radix-ui/react-dialog");o=e.t(o,1);function s(...e){return(0,a.twMerge)((0,i.clsx)(e))}var c=e=>{let t=new Date(e);return Number.isNaN(t.getTime())?``:t.toLocaleTimeString([],{hour:`numeric`,minute:`2-digit`})},l=(e,t)=>{e=new Date(e),t=new Date(t);let n=Math.floor(Math.abs(t.getTime()-e.getTime())/1e3);return n<60?`${n} second${n===1?``:`s`}`:n<3600?`${Math.floor(n/60)} minute${Math.floor(n/60)===1?``:`s`}`:n<86400?`${Math.floor(n/3600)} hour${Math.floor(n/3600)===1?``:`s`}`:n<31536e3?`${Math.floor(n/86400)} day${Math.floor(n/86400)===1?``:`s`}`:`${Math.floor(n/31536e3)} year${Math.floor(n/31536e3)===1?``:`s`}`};function u({...e}){return(0,n.jsx)(o.Root,{"data-slot":`dialog`,...e})}function d({...e}){return(0,n.jsx)(o.Trigger,{"data-slot":`dialog-trigger`,...e})}function f({...e}){return(0,n.jsx)(o.Portal,{"data-slot":`dialog-portal`,...e})}function p({className:e,...t}){return(0,n.jsx)(o.Overlay,{"data-slot":`dialog-overlay`,className:s(`data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 backdrop-blur-xs`,e),...t})}function m({className:e,children:t,hideCloseButton:i=!1,...a}){return(0,n.jsxs)(f,{"data-slot":`dialog-portal`,children:[(0,n.jsx)(p,{}),(0,n.jsxs)(o.Content,{"data-slot":`dialog-content`,"aria-describedby":a[`aria-describedby`]??void 0,className:s(`z-[9999] bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-2xl border p-6 shadow-lg duration-200`,e),...a,children:[t,!i&&(0,n.jsxs)(o.Close,{className:`ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4`,children:[(0,n.jsx)(r.XIcon,{}),(0,n.jsx)(`span`,{className:`sr-only`,children:`Close`})]})]})]})}function h({className:e,...t}){return(0,n.jsx)(o.Title,{"data-slot":`dialog-title`,className:s(`text-lg leading-none font-semibold`,e),...t})}function g({className:e,...t}){return(0,n.jsx)(o.Description,{"data-slot":`dialog-description`,className:s(`text-muted-foreground text-sm`,e),...t})}Object.defineProperty(exports,"a",{enumerable:!0,get:function(){return d}}),Object.defineProperty(exports,"c",{enumerable:!0,get:function(){return l}}),Object.defineProperty(exports,"i",{enumerable:!0,get:function(){return h}}),Object.defineProperty(exports,"n",{enumerable:!0,get:function(){return m}}),Object.defineProperty(exports,"o",{enumerable:!0,get:function(){return s}}),Object.defineProperty(exports,"r",{enumerable:!0,get:function(){return g}}),Object.defineProperty(exports,"s",{enumerable:!0,get:function(){return c}}),Object.defineProperty(exports,"t",{enumerable:!0,get:function(){return u}});
@@ -0,0 +1,80 @@
1
+ import "react";
2
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
3
+ import { XIcon as n } from "lucide-react";
4
+ import { clsx as r } from "clsx";
5
+ import { twMerge as i } from "tailwind-merge";
6
+ import * as a from "@radix-ui/react-dialog";
7
+ // src/lib/utils.ts
8
+ function o(...e) {
9
+ return i(r(e));
10
+ }
11
+ var s = (e) => {
12
+ let t = new Date(e);
13
+ return Number.isNaN(t.getTime()) ? "" : t.toLocaleTimeString([], {
14
+ hour: "numeric",
15
+ minute: "2-digit"
16
+ });
17
+ }, c = (e, t) => {
18
+ e = new Date(e), t = new Date(t);
19
+ let n = Math.floor(Math.abs(t.getTime() - e.getTime()) / 1e3);
20
+ return n < 60 ? `${n} second${n === 1 ? "" : "s"}` : n < 3600 ? `${Math.floor(n / 60)} minute${Math.floor(n / 60) === 1 ? "" : "s"}` : n < 86400 ? `${Math.floor(n / 3600)} hour${Math.floor(n / 3600) === 1 ? "" : "s"}` : n < 31536e3 ? `${Math.floor(n / 86400)} day${Math.floor(n / 86400) === 1 ? "" : "s"}` : `${Math.floor(n / 31536e3)} year${Math.floor(n / 31536e3) === 1 ? "" : "s"}`;
21
+ };
22
+ // src/ui/dialog.tsx
23
+ function l({ ...t }) {
24
+ return /* @__PURE__ */ e(a.Root, {
25
+ "data-slot": "dialog",
26
+ ...t
27
+ });
28
+ }
29
+ function u({ ...t }) {
30
+ return /* @__PURE__ */ e(a.Trigger, {
31
+ "data-slot": "dialog-trigger",
32
+ ...t
33
+ });
34
+ }
35
+ function d({ ...t }) {
36
+ return /* @__PURE__ */ e(a.Portal, {
37
+ "data-slot": "dialog-portal",
38
+ ...t
39
+ });
40
+ }
41
+ function f({ className: t, ...n }) {
42
+ return /* @__PURE__ */ e(a.Overlay, {
43
+ "data-slot": "dialog-overlay",
44
+ className: o("data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50 backdrop-blur-xs", t),
45
+ ...n
46
+ });
47
+ }
48
+ function p({ className: r, children: i, hideCloseButton: s = !1, ...c }) {
49
+ return /* @__PURE__ */ t(d, {
50
+ "data-slot": "dialog-portal",
51
+ children: [/* @__PURE__ */ e(f, {}), /* @__PURE__ */ t(a.Content, {
52
+ "data-slot": "dialog-content",
53
+ "aria-describedby": c["aria-describedby"] ?? void 0,
54
+ className: o("z-[9999] bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-2xl border p-6 shadow-lg duration-200", r),
55
+ ...c,
56
+ children: [i, !s && /* @__PURE__ */ t(a.Close, {
57
+ className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
58
+ children: [/* @__PURE__ */ e(n, {}), /* @__PURE__ */ e("span", {
59
+ className: "sr-only",
60
+ children: "Close"
61
+ })]
62
+ })]
63
+ })]
64
+ });
65
+ }
66
+ function m({ className: t, ...n }) {
67
+ return /* @__PURE__ */ e(a.Title, {
68
+ "data-slot": "dialog-title",
69
+ className: o("text-lg leading-none font-semibold", t),
70
+ ...n
71
+ });
72
+ }
73
+ function h({ className: t, ...n }) {
74
+ return /* @__PURE__ */ e(a.Description, {
75
+ "data-slot": "dialog-description",
76
+ className: o("text-muted-foreground text-sm", t),
77
+ ...n
78
+ });
79
+ }
80
+ export { u as a, c, m as i, p as n, o, h as r, s, l as t };
@@ -1,3 +1,4 @@
1
+ import { KokoroLoadProgress } from '../../lib/kokoro';
1
2
  type SpeechStatus = 'idle' | 'loading' | 'generating' | 'playing' | 'error';
2
3
  interface UseKokoroTTSOptions {
3
4
  autoPreload?: boolean;
@@ -8,6 +9,7 @@ export declare function useKokoroTTS(text: string, options?: UseKokoroTTSOptions
8
9
  voices: string[];
9
10
  selectedVoice: string;
10
11
  error: string;
12
+ progress: KokoroLoadProgress;
11
13
  warmupModel: () => Promise<void>;
12
14
  speak: () => Promise<void>;
13
15
  stop: () => void;
@@ -1,4 +1,4 @@
1
- type SpeechStatus = "started" | "stopped";
1
+ type SpeechStatus = "started" | "stopped" | "loading";
2
2
  interface TextToSpeechOptions {
3
3
  /** Enable mic-based interruption detection while TTS is playing. Only activates if mic permission is already granted. */
4
4
  enableInterrupt?: boolean;
@@ -7,6 +7,7 @@ interface TextToSpeechOptions {
7
7
  }
8
8
  export declare function useTextToSpeech(text: string, options?: TextToSpeechOptions): {
9
9
  speechStatus: SpeechStatus;
10
+ isLoading: boolean;
10
11
  start: () => Promise<void>;
11
12
  stop: () => void;
12
13
  };
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- "use client";"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./config.cjs"),t=require("./src-r4BsCIB4.cjs");exports.ARTICLE_TOOLBAR_SHORTCUTS=t.O,exports.ArticleAIResponse=t.T,exports.ArticleActionButtons=t.k,exports.ArticleContent=t.C,exports.ArticleFollowupQuestions=t.E,exports.ArticlePanelHeader=t.j,exports.ArticlePromptInput=t.D,exports.CategoryDock=t.o,exports.ChatInputBox=t.M,exports.ChatProvider=t.N,exports.ChatWindow=t.v,exports.CookieConsent=t.a,exports.ExtractPanelProvider=t.U,exports.HistoryDialogs=t.b,exports.HistoryDropdown=t.y,exports.KokoroVoiceSelector=t.c,exports.LexicalArticleViewer=t.w,exports.MainViewProvider=t.g,exports.QwkSearchApp=t.n,exports.QwkSearchProviders=t.r,exports.SPOTLIGHT_LINKS=t.h,exports.SPOTLIGHT_PREFIXES=t.f,exports.SessionProvider=t.P,exports.SpotlightPalette=t.u,exports.UnifiedMarkdown=t.S,exports.VoiceSettingsPanel=t.s,exports.cn=t.G,exports.configureResearchAgentUI=e.configureResearchAgentUI,exports.formatMessageTime=t.K,exports.formatTimeDifference=t.q,exports.formatToolbarShortcut=t.A,exports.matchSpotlight=t.p,exports.openSpotlight=t.d,exports.parsePrefix=t.m,exports.researchAgentUIConfig=e.researchAgentUIConfig,exports.useChat=t.I,exports.useChatState=t.L,exports.useChatTabs=t.t,exports.useChunkErrorReload=t.i,exports.useExtractPanel=t.W,exports.useHistoryState=t.x,exports.useKokoroTTS=t.l,exports.useMainView=t._,exports.useSession=t.F,exports.useTextToSpeech=t.R;
1
+ "use client";"use client";Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./config.cjs"),t=require("./src-ebmQu_if.cjs"),n=require("./tooltip-DmUO6ZUz.cjs"),r=require("./dialog-7GDz0sWx.cjs");exports.ARTICLE_TOOLBAR_SHORTCUTS=t.D,exports.ArticleAIResponse=t.w,exports.ArticleActionButtons=t.O,exports.ArticleContent=t.S,exports.ArticleFollowupQuestions=t.T,exports.ArticlePanelHeader=t.A,exports.ArticlePromptInput=t.E,exports.ArticleReaderMount=t.R,exports.CategoryDock=t.c,exports.ChatInputBox=t.U,exports.ChatProvider=t.$,exports.ChatWindow=t.L,exports.CookieConsent=t.s,exports.ExtractPanelProvider=n.i,exports.HistoryDialogs=t.B,exports.HistoryDropdown=t.z,exports.KokoroVoiceSelector=t.u,exports.LexicalArticleViewer=t.C,exports.MainViewProvider=t.y,exports.QWKSEARCH_DEFAULT_ORIGIN=t.j,exports.QwkSearchApp=t.n,exports.QwkSearchProviders=t.r,exports.SPOTLIGHT_LINKS=t.v,exports.SPOTLIGHT_PREFIXES=t.h,exports.SessionProvider=n.s,exports.SpotlightPalette=t.p,exports.UnifiedMarkdown=t.x,exports.VoiceSettingsPanel=t.l,exports.buildConnectStartUrl=t.M,exports.cn=r.o,exports.configureResearchAgentUI=e.configureResearchAgentUI,exports.createQwkSearchConnectAuthClient=t.N,exports.formatMessageTime=r.s,exports.formatTimeDifference=r.c,exports.formatToolbarShortcut=t.k,exports.getQwkSearchApiKey=t.P,exports.isChunkLoadError=t.i,exports.isQwkSearchApiRequest=t.F,exports.matchSpotlight=t.g,exports.openSpotlight=t.m,exports.parsePrefix=t._,exports.reloadForChunkError=t.a,exports.researchAgentUIConfig=e.researchAgentUIConfig,exports.setQwkSearchApiKey=t.I,exports.useChat=n.u,exports.useChatState=t.et,exports.useChatTabs=t.t,exports.useChunkErrorReload=t.o,exports.useExtractPanel=n.a,exports.useHistoryState=t.H,exports.useKokoroTTS=t.f,exports.useMainView=t.b,exports.useOptionalExtractPanel=n.o,exports.useSession=n.c,exports.useTextToSpeech=t.d;
package/dist/index.d.ts CHANGED
@@ -6,7 +6,11 @@ export type { Message, ChatTurn, UserMessage, AssistantMessage, SourceMessage, S
6
6
  export { ChatProvider, useChat, useChatState, } from './hooks/useChat';
7
7
  export type { ChatContextValue } from './hooks/useChat';
8
8
  export { SessionProvider, useSession } from './hooks/useSession';
9
- export { ExtractPanelProvider, useExtractPanel, } from './components/ArticleReader/ExtractPanelContext';
9
+ export { createQwkSearchConnectAuthClient, setQwkSearchApiKey, getQwkSearchApiKey, buildConnectStartUrl, isQwkSearchApiRequest, QWKSEARCH_DEFAULT_ORIGIN, } from './lib/qwksearch-connect';
10
+ export type { QwkSearchConnectSession, QwkSearchConnectOptions, QwkSearchConnectAuthClient, } from './lib/qwksearch-connect';
11
+ export { ExtractPanelProvider, useExtractPanel, useOptionalExtractPanel, } from './components/ArticleReader/ExtractPanelContext';
12
+ export type { OpenPanelOptions } from './components/ArticleReader/ExtractPanelContext';
13
+ export { default as ArticleReaderMount } from './components/ArticleReader/ArticleReaderMount';
10
14
  export * from './components/ArticleReader';
11
15
  export { default as HistoryDropdown } from './components/ChatHistoryDropdown';
12
16
  export { HistoryDialogs } from './components/ChatHistoryDropdown/HistoryDialogs';
package/dist/index.mjs CHANGED
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
  "use client";
3
3
  import { configureResearchAgentUI as e, researchAgentUIConfig as t } from "./config.mjs";
4
- import { A as n, C as r, D as i, E as a, F as o, G as s, I as c, K as l, L as u, M as d, N as f, O as p, P as m, R as h, S as g, T as _, U as v, W as y, _ as b, a as x, b as S, c as C, d as w, f as T, g as E, h as D, i as O, j as k, k as A, l as j, m as M, n as N, o as P, p as F, q as I, r as L, s as R, t as z, u as B, v as V, w as H, x as U, y as W } from "./src-HL9JjPc-.js";
5
- export { p as ARTICLE_TOOLBAR_SHORTCUTS, _ as ArticleAIResponse, A as ArticleActionButtons, r as ArticleContent, a as ArticleFollowupQuestions, k as ArticlePanelHeader, i as ArticlePromptInput, P as CategoryDock, d as ChatInputBox, f as ChatProvider, V as ChatWindow, x as CookieConsent, v as ExtractPanelProvider, S as HistoryDialogs, W as HistoryDropdown, C as KokoroVoiceSelector, H as LexicalArticleViewer, E as MainViewProvider, N as QwkSearchApp, L as QwkSearchProviders, D as SPOTLIGHT_LINKS, T as SPOTLIGHT_PREFIXES, m as SessionProvider, B as SpotlightPalette, g as UnifiedMarkdown, R as VoiceSettingsPanel, s as cn, e as configureResearchAgentUI, l as formatMessageTime, I as formatTimeDifference, n as formatToolbarShortcut, F as matchSpotlight, w as openSpotlight, M as parsePrefix, t as researchAgentUIConfig, c as useChat, u as useChatState, z as useChatTabs, O as useChunkErrorReload, y as useExtractPanel, U as useHistoryState, j as useKokoroTTS, b as useMainView, o as useSession, h as useTextToSpeech };
4
+ import { $ as n, A as r, B as i, C as a, D as o, E as s, F as c, H as l, I as u, L as d, M as f, N as p, O as m, P as h, R as g, S as _, T as v, U as y, _ as b, a as x, b as S, c as C, d as w, et as T, f as E, g as D, h as O, i as k, j as A, k as j, l as M, m as N, n as P, o as F, p as I, r as L, s as R, t as z, u as B, v as V, w as H, x as U, y as W, z as G } from "./src-GPr1ceY8.js";
5
+ import { a as K, c as q, i as J, o as Y, s as X, u as Z } from "./tooltip-Bl3IdX-I.js";
6
+ import { c as Q, o as $, s as ee } from "./dialog-Cufn2u50.js";
7
+ export { o as ARTICLE_TOOLBAR_SHORTCUTS, H as ArticleAIResponse, m as ArticleActionButtons, _ as ArticleContent, v as ArticleFollowupQuestions, r as ArticlePanelHeader, s as ArticlePromptInput, g as ArticleReaderMount, C as CategoryDock, y as ChatInputBox, n as ChatProvider, d as ChatWindow, R as CookieConsent, J as ExtractPanelProvider, i as HistoryDialogs, G as HistoryDropdown, B as KokoroVoiceSelector, a as LexicalArticleViewer, W as MainViewProvider, A as QWKSEARCH_DEFAULT_ORIGIN, P as QwkSearchApp, L as QwkSearchProviders, V as SPOTLIGHT_LINKS, O as SPOTLIGHT_PREFIXES, X as SessionProvider, I as SpotlightPalette, U as UnifiedMarkdown, M as VoiceSettingsPanel, f as buildConnectStartUrl, $ as cn, e as configureResearchAgentUI, p as createQwkSearchConnectAuthClient, ee as formatMessageTime, Q as formatTimeDifference, j as formatToolbarShortcut, h as getQwkSearchApiKey, k as isChunkLoadError, c as isQwkSearchApiRequest, D as matchSpotlight, N as openSpotlight, b as parsePrefix, x as reloadForChunkError, t as researchAgentUIConfig, u as setQwkSearchApiKey, Z as useChat, T as useChatState, z as useChatTabs, F as useChunkErrorReload, K as useExtractPanel, l as useHistoryState, E as useKokoroTTS, S as useMainView, Y as useOptionalExtractPanel, q as useSession, w as useTextToSpeech };
@@ -8,6 +8,14 @@
8
8
  import { AnyFileSource } from '../types/fileSource';
9
9
  export type { FileSource, AnyFileSource } from '../types/fileSource';
10
10
  export type { FileSourceType, SSHCredentials, S3Credentials, R2Credentials, B2Credentials, GoogleDocsCredentials, TursoDBCredentials, LocalFileSource, SSHFileSource, S3FileSource, R2FileSource, B2FileSource, GoogleDocsFileSource, TursoDBFileSource } from '../types/fileSource';
11
+ /**
12
+ * Dispatched on `window` whenever the saved source list changes, so a mounted
13
+ * sidebar picks up a source added in settings without a reload. The name is
14
+ * shared with the other copy of these helpers (research-agent-ui's
15
+ * `file-sources` and react-reason-editor-sidebar's) — both write the same
16
+ * localStorage key.
17
+ */
18
+ export declare const FILE_SOURCES_CHANGED_EVENT = "reason-file-sources-changed";
11
19
  export declare function getFileSources(): AnyFileSource[];
12
20
  export declare function saveFileSources(sources: AnyFileSource[]): void;
13
21
  export declare function addFileSource(source: Omit<AnyFileSource, 'id' | 'createdAt' | 'updatedAt'>): AnyFileSource;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * @fileoverview The "Google Drive" storage source on the client: connecting
3
+ * Drive with folder access, browsing folders to sync from settings, turning a
4
+ * synced folder's listing into file-tree documents, and the
5
+ * `FileSourceProvider` that hands those to `ReasonDocs`.
6
+ *
7
+ * Everything goes through this app's `/api/doc/google-docs/*` routes, which
8
+ * hold the Google tokens in HTTP-only cookies; no token reaches this code.
9
+ */
10
+ import type { Document, ExternalDocumentView } from 'react-reason-editor-sidebar';
11
+ import type { FileSourceProvider } from 'react-reason-editor/reason-docs';
12
+ import type { AnyFileSource, GoogleDocsCredentials } from '../types/fileSource';
13
+ /** Base path of the app's Google Drive routes. */
14
+ export declare const GOOGLE_DRIVE_API = "/api/doc/google-docs";
15
+ export declare const GOOGLE_FOLDER = "application/vnd.google-apps.folder";
16
+ /** One Drive file or folder as the folder-tree route returns it. */
17
+ export interface DriveItem {
18
+ id: string;
19
+ name: string;
20
+ mimeType: string;
21
+ parents?: string[];
22
+ webViewLink?: string;
23
+ size?: string;
24
+ modifiedTime?: string;
25
+ }
26
+ /** A folder chosen for sync. `root` stands for all of My Drive. */
27
+ export interface DriveFolderRef {
28
+ id: string;
29
+ name: string;
30
+ }
31
+ /** An error from a Drive route, with the flags the settings UI acts on. */
32
+ export declare class GoogleDriveError extends Error {
33
+ needsReauth: boolean;
34
+ needsFolderAccess: boolean;
35
+ status: number;
36
+ constructor(message: string, status: number, body?: Record<string, unknown>);
37
+ }
38
+ /**
39
+ * Pulls a Drive folder ID out of whatever the user pasted: a folder link
40
+ * (`drive.google.com/drive/folders/<id>`, with or without `/u/0`), an
41
+ * `?id=` link, "My Drive", or a bare ID. Returns `null` when nothing in it
42
+ * looks like a folder.
43
+ */
44
+ export declare function parseDriveFolderInput(input: string): string | null;
45
+ /** How a Drive file opens in the workspace. */
46
+ export declare function driveDocumentView(mimeType: string): ExternalDocumentView;
47
+ /** The extension that picks a Drive file's icon in the tree. */
48
+ export declare function driveExtension(item: Pick<DriveItem, 'name' | 'mimeType'>): string | undefined;
49
+ /** Document ID for a Drive file in a source: unique even when two sources share a file. */
50
+ export declare const driveDocumentId: (sourceId: string, fileId: string) => string;
51
+ /** Bytes (or the export) of a Drive file, served by the app. */
52
+ export declare const driveContentUrl: (fileId: string) => string;
53
+ /**
54
+ * Turns synced folders' listings into the flat, `parentId`-linked document
55
+ * list the file tree renders. Each synced folder becomes a top-level folder;
56
+ * a file reachable from two synced folders appears once, under the first.
57
+ * Folders sort before files, then by name, the way Drive shows them.
58
+ */
59
+ export declare function driveTreesToDocuments(sourceId: string, trees: Array<{
60
+ root: DriveItem;
61
+ items: DriveItem[];
62
+ }>): Document[];
63
+ /** The folders a Google Drive source syncs, newest field first. */
64
+ export declare function sourceFolders(credentials: Partial<GoogleDocsCredentials> | undefined): DriveFolderRef[];
65
+ /**
66
+ * Lists every synced folder of a Google Drive source as tree documents. A
67
+ * folder that fails (unshared, deleted) is skipped as long as another one
68
+ * loads; when all of them fail, the first error is thrown.
69
+ */
70
+ export declare function listGoogleDriveSource(source: AnyFileSource): Promise<Document[]>;
71
+ /**
72
+ * Cleans a Google Docs HTML export for the editor. Google styles text through
73
+ * generated CSS classes (`.c3{font-weight:700}`) in a `<style>` block, which
74
+ * the editor would drop along with the stylesheet — so bold, italic,
75
+ * underline and strikethrough are turned into tags first. Then every class,
76
+ * inline style and id goes, as do Google's `google.com/url?q=` link
77
+ * redirects, leaving plain semantic HTML.
78
+ */
79
+ export declare function googleDocHtmlToEditorHtml(html: string): string;
80
+ /** Fetches the body of a Drive document that opens in the editor. */
81
+ export declare function loadGoogleDriveDocumentContent(doc: Document): Promise<string>;
82
+ /** Shows Google Drive storage sources in the `ReasonDocs` file tree. */
83
+ export declare const googleDriveSourceProvider: FileSourceProvider;
84
+ /** Whether Drive is connected, and whether the grant covers folder sync. */
85
+ export declare function getGoogleDriveStatus(): Promise<{
86
+ isConnected: boolean;
87
+ canReadFolders: boolean;
88
+ }>;
89
+ /** One folder and the folders directly inside it, for the settings browser. */
90
+ export declare function browseGoogleDriveFolders(parentId?: string): Promise<{
91
+ folder: DriveFolderRef;
92
+ folders: DriveFolderRef[];
93
+ }>;
94
+ /**
95
+ * Connects Google Drive with folder access in a popup. The popup is opened
96
+ * before the consent URL is fetched, inside the click, so popup blockers let
97
+ * it through. Resolves `true` once the callback page reports success and
98
+ * `false` if the user closes the popup first.
99
+ */
100
+ export declare function connectGoogleDriveFolders(): Promise<boolean>;
@@ -5,8 +5,20 @@ interface Backend {
5
5
  dtype: DType;
6
6
  }
7
7
  export declare function getRecommendedBackend(): Backend;
8
- export declare function preloadKokoro(): Promise<any>;
9
- export declare function getKokoro(): Promise<any>;
8
+ export type KokoroLoadProgress = {
9
+ status: 'initiate' | 'download' | 'progress' | 'progress_total' | 'done' | 'ready';
10
+ name?: string;
11
+ file?: string;
12
+ progress?: number;
13
+ loaded?: number;
14
+ total?: number;
15
+ files?: Record<string, {
16
+ loaded: number;
17
+ total: number;
18
+ }>;
19
+ };
20
+ export declare function preloadKokoro(progressCallback?: (p: KokoroLoadProgress) => void): Promise<any>;
21
+ export declare function getKokoro(progressCallback?: (p: KokoroLoadProgress) => void): Promise<any>;
10
22
  export declare function getVoiceList(): Promise<string[]>;
11
23
  export declare function getLoadedBackend(): DeviceMode;
12
24
  export {};
@@ -0,0 +1,79 @@
1
+ /** Default QwkSearch origin the embed's API calls go to. */
2
+ export declare const QWKSEARCH_DEFAULT_ORIGIN = "https://qwksearch.com";
3
+ /** What the host's session endpoint answers. */
4
+ export interface QwkSearchConnectSession {
5
+ connected: boolean;
6
+ user?: {
7
+ id: string;
8
+ name: string;
9
+ email?: string;
10
+ image?: string | null;
11
+ } | null;
12
+ /** The linked user's QwkSearch API key. */
13
+ apiKey?: string | null;
14
+ /** Lower-case QwkSearch plan key (`"free"`, `"pro"`, …). */
15
+ plan?: string | null;
16
+ /** Stripe Payment Link that upgrades this QwkSearch account, when on a free plan. */
17
+ upgradeUrl?: string | null;
18
+ }
19
+ export interface QwkSearchConnectOptions {
20
+ sessionUrl: string;
21
+ connectUrl: string;
22
+ disconnectUrl: string;
23
+ /** Origin whose `/api/` requests get the key. Defaults to qwksearch.com. */
24
+ qwksearchOrigin?: string;
25
+ /** Called with every session the host reports (e.g. to show the plan). */
26
+ onSession?: (session: QwkSearchConnectSession) => void;
27
+ }
28
+ /** True when `input` targets `<origin>/api/…`. */
29
+ export declare function isQwkSearchApiRequest(input: RequestInfo | URL, origin: string): boolean;
30
+ /**
31
+ * Sets (or with `null`, clears) the QwkSearch API key the embed sends.
32
+ * `origin` defaults to qwksearch.com.
33
+ */
34
+ export declare function setQwkSearchApiKey(key: string | null, origin?: string): void;
35
+ /** The key currently attached to QwkSearch API requests, if any. */
36
+ export declare function getQwkSearchApiKey(): string | null;
37
+ /** Where `connectUrl` should send the visitor back to: the page they are on. */
38
+ export declare function buildConnectStartUrl(connectUrl: string, returnTo?: string): string;
39
+ /**
40
+ * An auth client for `SessionProvider` (and better-auth-style
41
+ * `useSession()` callers) that signs in through QwkSearch's connect flow.
42
+ */
43
+ export declare function createQwkSearchConnectAuthClient(options: QwkSearchConnectOptions): {
44
+ /** The last session the host reported (no request). */
45
+ getCachedSession: () => QwkSearchConnectSession;
46
+ /** Re-reads the host's session endpoint (e.g. after an upgrade). */
47
+ refresh: () => Promise<QwkSearchConnectSession>;
48
+ /** Subscribes to session changes; returns an unsubscribe function. */
49
+ subscribe: (listener: () => void) => () => boolean;
50
+ getSession: () => Promise<{
51
+ data: {
52
+ user: {
53
+ id: string;
54
+ name: string;
55
+ email?: string;
56
+ image?: string | null;
57
+ };
58
+ };
59
+ }>;
60
+ oneTap: (_opts?: {
61
+ fetchOptions: {
62
+ onSuccess: () => void;
63
+ };
64
+ }) => void;
65
+ signIn: {
66
+ social: (_opts?: {
67
+ provider: string;
68
+ callbackURL: string;
69
+ }) => void;
70
+ };
71
+ signOut: (opts?: {
72
+ fetchOptions?: {
73
+ onSuccess?: () => void;
74
+ };
75
+ }) => Promise<{
76
+ data: any;
77
+ }>;
78
+ };
79
+ export type QwkSearchConnectAuthClient = ReturnType<typeof createQwkSearchConnectAuthClient>;
@@ -57,3 +57,18 @@ export declare function getSettingsSchema(): {
57
57
  sections: SettingsSectionSchema[];
58
58
  fieldsBySection: Record<string, SettingsFieldSchema[]>;
59
59
  };
60
+ /**
61
+ * One settings section as a standalone page a host can mount in its own
62
+ * settings area — the "external page" form of {@link SettingsSectionSchema}.
63
+ */
64
+ export interface SettingsPageSchema extends SettingsSectionSchema {
65
+ /** Route the host serves this section at, e.g. `/settings/research/models`. */
66
+ href: string;
67
+ }
68
+ /**
69
+ * The settings sections as pages under `basePath`, in menu order. A host that
70
+ * embeds the research agent registers these alongside its own settings pages
71
+ * (`basePath` is where it mounts the research settings), so every section the
72
+ * agent exposes is reachable from the host's global settings.
73
+ */
74
+ export declare function getSettingsPages(basePath?: string): SettingsPageSchema[];