feat(desktop): polish desktop shell and shared WebUI surfaces (#4195)

* feat(desktop): add native host scaffold

* feat(webui): track turns and usage in gateway

* feat(webui): polish desktop chat experience

* feat(apps): add ArcGIS and Joplin logos

* feat(desktop): polish shell and shared surfaces

* fix(webui): avoid preview chips for glob references

* test: align CI expectations for token fallback

* feat(webui): preview prompt rail entries

* feat(webui): add prompt navigator drawer

* style(webui): refine prompt navigator placement

* style(webui): align prompt navigator with header actions

* style(webui): simplify prompt navigator header

* refactor(webui): clean thread resource refresh

* feat(desktop): add native reply notifications

* fix(webui): preserve desktop restart and replay state

* fix(desktop): harden gateway proxy startup

* fix(web): fall back when readability is unavailable

* fix(desktop): hide window instead of closing on macos

* fix(webui): unify desktop header actions

* fix(webui): simplify prompt history rows

* fix(desktop): log notification delivery failures

* chore(desktop): clean source package artifacts

* fix(cron): support one-time relative reminders

* fix(webui): reveal scroll button in place

* Revert "fix(cron): support one-time relative reminders"

This reverts commit 4c4661da120a3c7283e0768412bae48604e7390b.

* refactor(webui): extract token usage heatmap

* docs(desktop): clarify contributor guides

---------

Co-authored-by: chengyongru <2755839590@qq.com>
This commit is contained in:
Xubin Ren
2026-06-06 19:49:33 +08:00
committed by GitHub
co-authored by chengyongru
parent a1b9577224
commit ab9f49970d
103 changed files with 10483 additions and 1003 deletions
+147 -40
View File
@@ -9,15 +9,33 @@ interface CodeBlockProps {
language?: string;
code: string;
className?: string;
chrome?: "default" | "none";
highlight?: boolean;
showLineNumbers?: boolean;
wrapLongLines?: boolean;
}
interface HighlightedCodeProps {
language?: string;
code: string;
isDark: boolean;
chrome: "default" | "none";
showLineNumbers: boolean;
wrapLongLines: boolean;
}
const CODE_FONT_STACK = [
'"JetBrains Mono"',
'"SFMono-Regular"',
'"SF Mono"',
'"Fira Code"',
'"Cascadia Code"',
'"Source Code Pro"',
"Menlo",
"Consolas",
"monospace",
].join(", ");
const LazyHighlightedCode = lazy(async () => {
const [
{ default: SyntaxHighlighter },
@@ -30,19 +48,56 @@ const LazyHighlightedCode = lazy(async () => {
]);
return {
default({ language, code, isDark }: HighlightedCodeProps) {
default({
language,
code,
isDark,
chrome,
showLineNumbers,
wrapLongLines,
}: HighlightedCodeProps) {
const theme = isDark ? oneDark : oneLight;
const transparentTheme = chrome === "none" ? {
...theme,
'pre[class*="language-"]': {
...theme['pre[class*="language-"]'],
background: "transparent",
},
'code[class*="language-"]': {
...theme['code[class*="language-"]'],
background: "transparent",
},
} : theme;
return (
<SyntaxHighlighter
language={language || "text"}
style={isDark ? oneDark : oneLight}
style={transparentTheme}
customStyle={{
background: chrome === "none" ? "transparent" : undefined,
margin: 0,
padding: "1rem",
fontSize: "0.875rem",
lineHeight: 1.6,
padding: chrome === "none" ? "0.75rem 1rem" : "1rem",
fontFamily: CODE_FONT_STACK,
fontSize: chrome === "none" ? "13px" : "0.875rem",
lineHeight: chrome === "none" ? 1.55 : 1.6,
tabSize: 2,
}}
codeTagProps={{
style: chrome === "none" ? {
background: "transparent",
fontFamily: CODE_FONT_STACK,
} : undefined,
}}
lineNumberStyle={{
minWidth: "2.6em",
paddingRight: "1.15rem",
color: isDark ? "rgba(212, 212, 216, 0.45)" : "rgba(63, 63, 70, 0.68)",
fontFamily: CODE_FONT_STACK,
userSelect: "none",
}}
PreTag="pre"
wrapLongLines
showLineNumbers={showLineNumbers}
wrapLongLines={wrapLongLines}
>
{code}
</SyntaxHighlighter>
@@ -51,13 +106,39 @@ const LazyHighlightedCode = lazy(async () => {
};
});
function PlainCodeFallback({ code }: { code: string }) {
function PlainCodeFallback({
code,
chrome,
showLineNumbers,
}: {
code: string;
chrome: "default" | "none";
showLineNumbers: boolean;
}) {
const lines = code.split("\n");
return (
<pre
className="m-0 overflow-x-auto whitespace-pre-wrap bg-background p-4 font-mono text-sm leading-[1.6] text-foreground/90"
className={cn(
"m-0 overflow-x-auto p-4 font-mono text-sm leading-[1.6] text-foreground/90",
showLineNumbers ? "whitespace-pre" : "whitespace-pre-wrap",
chrome === "default" ? "bg-background" : "bg-transparent",
chrome === "none" && "p-3 text-[13px] leading-[1.55]",
)}
data-testid="plain-code-fallback"
>
<code className="text-inherit">{code}</code>
<code className="text-inherit">
{showLineNumbers ? (
lines.map((line, index) => (
<span key={index} className="flex min-w-max">
<span className="w-10 shrink-0 select-none pr-4 text-right text-muted-foreground/60">
{index + 1}
</span>
<span className="whitespace-pre">{line || " "}</span>
{index < lines.length - 1 ? "\n" : null}
</span>
))
) : code}
</code>
</pre>
);
}
@@ -66,11 +147,15 @@ export function CodeBlock({
language,
code,
className,
chrome = "default",
highlight = true,
showLineNumbers = false,
wrapLongLines = true,
}: CodeBlockProps) {
const { t } = useTranslation();
const [copied, setCopied] = useState(false);
const isDark = useThemeValue() === "dark";
const hasChrome = chrome === "default";
const onCopy = useCallback(() => {
if (!navigator.clipboard) return;
@@ -83,47 +168,69 @@ export function CodeBlock({
return (
<div
className={cn(
"overflow-hidden rounded-lg border",
isDark ? "border-white/10" : "border-black/10",
"overflow-hidden",
hasChrome && "rounded-lg border",
hasChrome && (isDark ? "border-white/10" : "border-black/10"),
className,
)}
>
<div
className={cn(
"flex items-center justify-between px-4 py-1.5 text-xs font-medium",
isDark
? "bg-zinc-800 text-zinc-300"
: "bg-zinc-100 text-zinc-600",
)}
>
<span className="lowercase font-mono">
{language || t("code.fallbackLanguage")}
</span>
<button
type="button"
onClick={onCopy}
{hasChrome ? (
<div
className={cn(
"inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono transition-colors",
"flex items-center justify-between px-4 py-1.5 text-xs font-medium",
isDark
? "text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200"
: "text-zinc-500 hover:bg-zinc-200 hover:text-zinc-700",
? "bg-zinc-800 text-zinc-300"
: "bg-zinc-100 text-zinc-600",
)}
aria-label={t("code.copyAria")}
>
{copied ? (
<Check className="h-3.5 w-3.5" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
<span>{copied ? t("code.copied") : t("code.copy")}</span>
</button>
</div>
<span className="lowercase font-mono">
{language || t("code.fallbackLanguage")}
</span>
<button
type="button"
onClick={onCopy}
className={cn(
"inline-flex items-center gap-1 rounded px-1.5 py-0.5 font-mono transition-colors",
isDark
? "text-zinc-400 hover:bg-zinc-700 hover:text-zinc-200"
: "text-zinc-500 hover:bg-zinc-200 hover:text-zinc-700",
)}
aria-label={t("code.copyAria")}
>
{copied ? (
<Check className="h-3.5 w-3.5" />
) : (
<Copy className="h-3.5 w-3.5" />
)}
<span>{copied ? t("code.copied") : t("code.copy")}</span>
</button>
</div>
) : null}
{highlight ? (
<Suspense fallback={<PlainCodeFallback code={code} />}>
<LazyHighlightedCode language={language} code={code} isDark={isDark} />
<Suspense
fallback={
<PlainCodeFallback
code={code}
chrome={chrome}
showLineNumbers={showLineNumbers}
/>
}
>
<LazyHighlightedCode
language={language}
code={code}
isDark={isDark}
chrome={chrome}
showLineNumbers={showLineNumbers}
wrapLongLines={wrapLongLines}
/>
</Suspense>
) : (
<PlainCodeFallback code={code} />
<PlainCodeFallback
code={code}
chrome={chrome}
showLineNumbers={showLineNumbers}
/>
)}
</div>
);