fix(assistant): mysql message storage, migration metadata and per-page bundle cost (#6530)
This commit is contained in:
1 parent
f6f755d5c9
commit
a9b0c2ae47
14 files changed
+527
-129
No files matched your search
@@ -0,0 +1,79 @@
|
||||
"use client";
|
||||
|
||||
import type { PropsWithChildren } from "react";
|
||||
import { useMemo } from "react";
|
||||
import dynamic from "next/dynamic";
|
||||
|
||||
import { useScopedI18n } from "@homarr/translation/client";
|
||||
import { AssistantWidgetRendererProvider } from "@homarr/widgets";
|
||||
|
||||
import { AssistantContext } from "./assistant-context";
|
||||
import { AssistantBoardWidgetLazy } from "./assistant-widget-lazy";
|
||||
import { useRegisterAssistantSpotlightPlaceholder } from "./assistant-spotlight-placeholder";
|
||||
|
||||
/**
|
||||
* The enabled runtime is the expensive half of the assistant (assistant-ui, Lexical, Markdown,
|
||||
* Mermaid). Loading it through `next/dynamic` puts it in its own chunk that is only requested when
|
||||
* this component actually renders. Because `enabled` is resolved on the server and handed down as a
|
||||
* prop, an instance with the assistant switched off never renders it, so the chunk is never part of
|
||||
* the page payload. SSR stays on so enabled instances render children in the initial HTML without a
|
||||
* loading flash or a remount once the chunk arrives.
|
||||
*/
|
||||
const EnabledAssistantRoot = dynamic(() =>
|
||||
import("./assistant-provider").then((module) => ({ default: module.EnabledAssistantRoot })),
|
||||
);
|
||||
|
||||
const DisabledAssistant = ({ children, description }: PropsWithChildren<{ description: string }>) => {
|
||||
useRegisterAssistantSpotlightPlaceholder(description);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
enabled: false,
|
||||
unavailableDescription: description,
|
||||
opened: false,
|
||||
isRunning: false,
|
||||
isRefreshing: false,
|
||||
unreadCount: 0,
|
||||
open: () => undefined,
|
||||
close: () => undefined,
|
||||
toggle: () => undefined,
|
||||
sendPrompt: () => false,
|
||||
refreshCurrentView: () => Promise.resolve(),
|
||||
}),
|
||||
[description],
|
||||
);
|
||||
|
||||
return (
|
||||
<AssistantContext.Provider value={value}>
|
||||
<AssistantWidgetRendererProvider renderer={AssistantBoardWidgetLazy}>{children}</AssistantWidgetRendererProvider>
|
||||
</AssistantContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Why the assistant is or is not usable, resolved on the server.
|
||||
*
|
||||
* `unauthenticated` is kept apart from `unconfigured` because the server cannot check availability
|
||||
* for a signed-out visitor, and telling them the instance is unconfigured would be a guess.
|
||||
*/
|
||||
export type AssistantAvailability = "enabled" | "unconfigured" | "unauthenticated" | "error";
|
||||
|
||||
interface AssistantGateProps extends PropsWithChildren {
|
||||
availability: AssistantAvailability;
|
||||
}
|
||||
|
||||
const unavailableMessageKeys = {
|
||||
unauthenticated: "unavailable.signIn",
|
||||
unconfigured: "unavailable.notConfigured",
|
||||
error: "unavailable.error",
|
||||
} as const;
|
||||
|
||||
export const AssistantGate = ({ availability, children }: AssistantGateProps) => {
|
||||
const t = useScopedI18n("common.assistant");
|
||||
|
||||
if (availability === "enabled") {
|
||||
return <EnabledAssistantRoot>{children}</EnabledAssistantRoot>;
|
||||
}
|
||||
|
||||
return <DisabledAssistant description={t(unavailableMessageKeys[availability])}>{children}</DisabledAssistant>;
|
||||
};
|
||||
Reference in new issue
Block a user