-
+
{{ viewer.snapshot.value.url }}
diff --git a/plugins/og/app/app/components/previews/TelegramPreview.vue b/plugins/og/app/app/components/previews/TelegramPreview.vue
index 107b4ac84..34577c447 100644
--- a/plugins/og/app/app/components/previews/TelegramPreview.vue
+++ b/plugins/og/app/app/components/previews/TelegramPreview.vue
@@ -11,12 +11,12 @@ const time = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-di
- {{ card.url }}
+ {{ card.url }}
-
+
-
+
{{ card.hostname }}
diff --git a/plugins/og/app/main.ts b/plugins/og/app/main.ts
index 41492cdef..bb7a8a788 100644
--- a/plugins/og/app/main.ts
+++ b/plugins/og/app/main.ts
@@ -1,4 +1,5 @@
import { createApp } from 'vue'
+import { syncPanelTheme } from '../../../design/panel-theme'
import App from './app/app.vue'
const media = window.matchMedia('(prefers-color-scheme: dark)')
@@ -10,3 +11,6 @@ applyScheme(media.matches)
media.addEventListener('change', event => applyScheme(event.matches))
createApp(App).mount('#app')
+
+const stopPanelTheme = syncPanelTheme()
+import.meta.hot?.dispose(stopPanelTheme)
diff --git a/plugins/terminals/app/main.ts b/plugins/terminals/app/main.ts
index d545c981c..4987fc077 100644
--- a/plugins/terminals/app/main.ts
+++ b/plugins/terminals/app/main.ts
@@ -1,3 +1,6 @@
+///
+
+import { syncPanelTheme } from '../../../design/panel-theme'
import { mountTerminals } from './client/index'
const app = document.getElementById('app')
@@ -7,3 +10,6 @@ if (!app)
mountTerminals(app).catch((error) => {
app.textContent = `Failed to connect: ${error instanceof Error ? error.message : String(error)}`
})
+
+const stopPanelTheme = syncPanelTheme()
+import.meta.hot?.dispose(stopPanelTheme)
diff --git a/tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts
index 0eebf21b1..5aab5f959 100644
--- a/tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts
+++ b/tests/__snapshots__/tsnapi/devframe/client.snapshot.d.ts
@@ -111,6 +111,13 @@ export interface DevframeServicesClient {
keys: () => string[];
state: () => Promise>;
}
+export interface DevframeTheme {
+ primaryColor?: string;
+}
+export interface DevframeThemeProvider {
+ update: (_: DevframeTheme) => void;
+ dispose: () => void;
+}
export interface RegisterWebMcpToolsOptions {
modelContext?: WebMcpModelContext;
}
@@ -208,6 +215,7 @@ export declare function createScopedClientContext(_:
export declare function getDevframeConnection(): DevframeConnection | undefined;
export declare function getDevframeRpcClient(_?: DevframeRpcClientOptions): Promise;
export declare function isCallableStatus(_: DevframeConnectionStatus): boolean;
+export declare function provideDevframeTheme(_: HTMLIFrameElement, _: DevframeTheme): DevframeThemeProvider;
export declare function readOtpFromUrl(_?: string): string | undefined;
export declare function registerDevframeViewerOrigin(_: DevframeConnection, _?: any): Promise;
export declare function registerWebMcpTools(_: RpcFunctionsCollector, _?: RegisterWebMcpToolsOptions): () => void;
@@ -216,6 +224,7 @@ export declare function resolveSseUrl(_: ConnectionMeta['sse'], _: string, _: Ws
export declare function resolveWebMcpModelContext(): WebMcpModelContext | undefined;
export declare function resolveWsUrl(_: ConnectionMeta['websocket'], _: string, _: WsUrlLocation): string;
export declare function setupDevframeConnection(_?: SetupDevframeConnectionOptions): Promise;
+export declare function watchDevframeTheme(_: (_: DevframeTheme) => void): () => void;
// #endregion
// #region Variables
diff --git a/tests/__snapshots__/tsnapi/devframe/client.snapshot.js b/tests/__snapshots__/tsnapi/devframe/client.snapshot.js
index 5624d64ba..b77abd471 100644
--- a/tests/__snapshots__/tsnapi/devframe/client.snapshot.js
+++ b/tests/__snapshots__/tsnapi/devframe/client.snapshot.js
@@ -18,6 +18,7 @@ export function createScopedClientContext(_, _) {}
export function getDevframeConnection() {}
export async function getDevframeRpcClient(_) {}
export function isCallableStatus(_) {}
+export function provideDevframeTheme(_, _) {}
export function readOtpFromUrl(_) {}
export async function registerDevframeViewerOrigin(_, _) {}
export function registerWebMcpTools(_, _) {}
@@ -26,6 +27,7 @@ export function resolveSseUrl(_, _, _) {}
export function resolveWebMcpModelContext() {}
export function resolveWsUrl(_, _, _) {}
export async function setupDevframeConnection(_) {}
+export function watchDevframeTheme(_) {}
// #endregion
// #region Variables
diff --git a/tests/__snapshots__/tsnapi/devframe/constants.snapshot.d.ts b/tests/__snapshots__/tsnapi/devframe/constants.snapshot.d.ts
index d3f0cf78c..8129c5831 100644
--- a/tests/__snapshots__/tsnapi/devframe/constants.snapshot.d.ts
+++ b/tests/__snapshots__/tsnapi/devframe/constants.snapshot.d.ts
@@ -38,6 +38,7 @@ export declare const DEVFRAME_EVENTS: {
readonly panelStatePatch: "devframe:in-page:panel-state:patch";
};
readonly postMessage: {
+ readonly theme: "devframe:theme";
readonly remoteAssetsError: "devframe:remote-assets-error";
readonly inPageChannel: "devframe:in-page-channel";
};
From b6adba6a2de34ae56d08726f152b46c707136929 Mon Sep 17 00:00:00 2001
From: Sakana <15715093608@163.com>
Date: Sun, 13 Sep 2026 13:11:55 +0800
Subject: [PATCH 2/3] refactor(design): name fixed status and preview color
shortcuts
---
design/uno.config.ts | 4 ++++
plugins/git/app/components/ui/status-mark.tsx | 4 ++--
plugins/og/app/app/app.vue | 2 +-
plugins/og/app/app/components/previews/TelegramPreview.vue | 6 +++---
4 files changed, 10 insertions(+), 6 deletions(-)
diff --git a/design/uno.config.ts b/design/uno.config.ts
index 80d3f08aa..2c2f6e547 100644
--- a/design/uno.config.ts
+++ b/design/uno.config.ts
@@ -66,6 +66,10 @@ export function createDesignConfig(options: CreateDesignConfigOptions = {}) {
colors: { devframe: resolvePrimary('#3a6a45') },
},
shortcuts: {
+ /** Fixed semantic colors stay independent of the panel's primary accent. */
+ 'color-status-positive': 'color-devframe-600 dark:color-devframe-300',
+ 'color-preview-accent': 'color-devframe-600 dark:color-devframe-300',
+ 'bg-preview-accent': 'bg-devframe',
/** Fixed navbar height, shared by every surface's top nav. */
'h-nav': 'h-10',
/** Named z-index layers, shared across every surface. */
diff --git a/plugins/git/app/components/ui/status-mark.tsx b/plugins/git/app/components/ui/status-mark.tsx
index a25e8e3d5..08db8e726 100644
--- a/plugins/git/app/components/ui/status-mark.tsx
+++ b/plugins/git/app/components/ui/status-mark.tsx
@@ -20,8 +20,8 @@ const STATUS_COLOR: Record = {
'added': 'text-success',
'deleted': 'text-error',
'modified': 'text-warning',
- 'renamed': 'color-devframe-600 dark:color-devframe-300',
- 'copied': 'color-devframe-600 dark:color-devframe-300',
+ 'renamed': 'color-status-positive',
+ 'copied': 'color-status-positive',
'type-changed': 'text-warning',
'unmerged': 'text-error',
'unknown': 'color-muted',
diff --git a/plugins/og/app/app/app.vue b/plugins/og/app/app/app.vue
index 7e4d01dd4..3f2ca6dcf 100644
--- a/plugins/og/app/app/app.vue
+++ b/plugins/og/app/app/app.vue
@@ -49,7 +49,7 @@ onMounted(() => viewer.inspect())
-
+
{{ viewer.snapshot.value.url }}
diff --git a/plugins/og/app/app/components/previews/TelegramPreview.vue b/plugins/og/app/app/components/previews/TelegramPreview.vue
index 34577c447..ee6ad7999 100644
--- a/plugins/og/app/app/components/previews/TelegramPreview.vue
+++ b/plugins/og/app/app/components/previews/TelegramPreview.vue
@@ -11,12 +11,12 @@ const time = new Intl.DateTimeFormat(undefined, { hour: '2-digit', minute: '2-di
- {{ card.url }}
+ {{ card.url }}
-
+
-
+
{{ card.hostname }}
From e813c3d1c6ef67424822bbd3ce9ea5ac79697ae1 Mon Sep 17 00:00:00 2001
From: Sakana <15715093608@163.com>
Date: Sun, 13 Sep 2026 13:12:17 +0800
Subject: [PATCH 3/3] docs: index the panel theme API reference
---
docs/content/8.references/index.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/content/8.references/index.md b/docs/content/8.references/index.md
index bc01b8fa6..5252db962 100644
--- a/docs/content/8.references/index.md
+++ b/docs/content/8.references/index.md
@@ -11,7 +11,7 @@ Lookup pages the guides link into:
- [When Clauses](/references/when-clauses): the contexts and operators that gate docks, commands, and custom UI.
- [Events Reference](/references/events): every event, broadcast, shared-state key, and channel name, by direction and reach.
- [Node-Side API](/references/node-api): `DevframeDefinition` fields, CLI options, storage scopes, RPC function types, broadcast options, streaming lifecycle, remote assets, the `ctx.services` host and wire-service fields, diagnostics prefixes, and the auth surface.
-- [Browser-Side API](/references/browser-api): `connectDevframe` options, RPC client events, connection statuses, and in-page channel error codes.
+- [Browser-Side API](/references/browser-api): `connectDevframe` options, RPC client events, connection statuses, panel theme APIs, and in-page channel error codes.
- [Hub API](/references/hub-api): hub subsystems, launcher fields, duplication strategies, dock categories, the hub UI protocol, the namespace routes, the client runtime, the client context, and dock entry types.
- [Utilities](/references/utilities): the small, stable helpers under `devframe/utils/*`, bundled into `devframe`.
- [Interactive Auth](/references/interactive-auth): the OTP auth recipe: handshake, resolver gate, connect-time trust, banner.