Chuyển tới nội dung chính

Mở rộng Bảng điều khiển

#Mở rộng Bảng điều khiển

Bảng điều khiển web Hermes ( `Hermes dashboard

) được xây dựng để thay đổi giao diện và mở rộng mà không cần phân tách cơ sở mã. Ba lớp được phơi bày:

  1. Chủ đề — Tệp YAML sơn lại bảng màu, kiểu chữ, bố cục và chrome theo từng thành phần của trang tổng quan. Thả một tệp vào

~/.Hermes/dashboard-themes/

; nó xuất hiện trong trình chuyển đổi chủ đề. 2. plugin giao diện người dùng — một thư mục có `manifest.JSON

  • gói JavaScript đăng ký một tab, thay thế một trang tích hợp sẵn, bổ sung một trang thông qua các vị trí trong phạm vi trang hoặc đưa các thành phần vào các vị trí shell được đặt tên.
  1. Plugin phụ trợ — một tệp Python bên trong thư mục plugin đó hiển thị FastAPI `router

; các tuyến đường được gắn trong

/API/plugins/<name/ ` và được gọi từ giao diện người dùng của plugin.

Cả ba đều dùng trong thời gian chạy: không sao chép repo, không `npm run build

, không vá nguồn bảng điều khiển. Trang này là tài liệu tham khảo chuẩn cho cả ba.

Nếu bạn chỉ muốn sử dụng trang tổng quan, hãy xem Web Dashboard. Nếu bạn muốn thay đổi giao diện của terminal CLI (không phải trang tổng quan web), hãy xem Skins & Themes - hệ thống giao diện CLI không liên quan đến chủ đề trang tổng quan.

How the pieces compose

Chủ đề và plugin độc lập nhưng có tác dụng hiệp đồng. Một chủ đề có thể độc lập (chỉ là một tệp YAML). Một plugin có thể độc lập (chỉ một tab). Chúng cùng nhau cho phép bạn xây dựng một giao diện trực quan hoàn chỉnh với HUD tùy chỉnh — bản demo strike-freedom-cockpit đi kèm thực hiện chính xác điều đó. Xem Combined theme + plugin demo.


Mục lục


Chủ đề

Chủ đề là các tệp YAML được lưu trữ trong

~/.Hermes/dashboard-themes/

. Tên tệp không quan trọng (trường name: của chủ đề là tên hệ thống sử dụng), nhưng quy ước là

<name.YAML

. Mọi trường đều là tùy chọn — các phím bị thiếu sẽ quay trở lại chủ đề default tích hợp sẵn, do đó, chủ đề có thể chỉ nhỏ bằng một màu.

Bắt đầu nhanh — chủ đề đầu tiên của bạn

mkdir -p ~/.Hermes/dashboard-themes

`

`
`YAML

# ~/.Hermes/dashboard-themes/neon.YAML
name: neon
label: Neon
description: Pure magenta on black`palette:
background: "#000000"
midground: "#ff00ff"

`
``Làm mới bảng điều khiển. Nhấp vào biểu tượng bảng màu trong tiêu đề và chọn **Neon**. Nền chuyển sang màu đen, văn bản và các điểm nhấn chuyển sang màu đỏ tươi và mọi màu dẫn xuất (thẻ, đường viền, tắt tiếng, vòng, v.v.) được tính toán lại từ bộ ba 2 màu đó thông qua
`color-mix()
` trong CSS.

Đó là toàn bộ quá trình triển khai: một tệp, hai màu. Mọi thứ bên dưới là sàng lọc tùy chọn.

### Bảng màu, kiểu chữ, bố cục

Ba khối này là trung tâm của một chủ đề. Mỗi cái đều độc lập - ghi đè một cái, để lại những cái khác.

#### Bảng màu (3 lớp)

Bảng màu là bộ ba lớp màu cộng với màu họa tiết rực rỡ ấm áp và hệ số nhiễu hạt. Tầng hệ thống thiết kế của bảng điều khiển lấy ra mọi mã thông báo tương thích với shadcn (thẻ, cửa sổ bật lên, tắt tiếng, đường viền, chính, phá hoại, đổ chuông, v.v.) từ bộ ba này thông qua CSS
`color-mix()

. Ghi đè ba màu sẽ xếp tầng vào toàn bộ giao diện người dùng.

| Chìa khóa | Mô tả |
|------|-------------|
|
`palette.background
` | Màu canvas đậm nhất - thường gần như đen. Điều khiển nền trang và điền thẻ. |
|
`palette.midground
` | Văn bản chính và trọng âm. Hầu hết giao diện người dùng Chrome đều đọc nội dung này (văn bản nền trước, đường viền nút, vòng lấy nét). |
|
`palette.foreground
` | Điểm nổi bật của lớp trên cùng. Chủ đề mặc định đặt chủ đề này thành màu trắng ở alpha 0 (ẩn); các chủ đề muốn có điểm nhấn tươi sáng ở trên có thể nâng cao độ alpha của nó. |
|
`palette.warmGlow
` | Chuỗi
`rgba(...)
` được

&lt;Backdrop />
` sử dụng làm màu họa tiết. |
|
`palette.noiseOpacity
` | Hệ số nhân 0–1,2 trên lớp phủ hạt. Thấp hơn = mềm hơn, cao hơn = cứng hơn. |

Mỗi lớp chấp nhận

\{hex: "#RRGGBB", alpha: 0.0–1.0}
` hoặc chuỗi hex trần (alpha mặc định là 1.0).

``` yaml
palette:
background:
hex: "#05091a"
alpha: 1.0
midground: "#d8f0ff" # bare hex, alpha = 1.0
foreground:
hex: "#ffffff"
alpha: 0 # invisible top layer
warmGlow: "rgba(255, 199, 55, 0.24)"
noiseOpacity: 0.7

`

#### Kiểu chữ| Chìa khóa | Loại | Mô tả |
|------|------|-------------|
|
`fontSans
` | chuỗi | Ngăn xếp họ phông chữ CSS cho bản sao nội dung (áp dụng cho
`html

,
`body

). |
|
`fontMono
` | chuỗi | Ngăn xếp họ phông chữ CSS cho các khối mã, tiện ích

<code>

,

.font-mono

. |
|
`fontDisplay
` | chuỗi | Ngăn xếp tiêu đề/hiển thị tùy chọn. Trở lại
`fontSans

. |
|
`fontUrl
` | chuỗi | URL biểu định kiểu bên ngoài tùy chọn. Được đưa vào dưới dạng

&lt;link rel="stylesheet">
` trong

&lt;head>
` trên nút chuyển chủ đề. Cùng một URL không bao giờ được tiêm hai lần. Hoạt động với Google Fonts, Bunny Fonts, trang tính

@font-face
` tự lưu trữ — mọi thứ có thể liên kết được. |
|
`baseSize
` | chuỗi | Kích thước phông chữ gốc - kiểm soát thang đo rem. Ví dụ.

"14px"

,

"16px"

. |
|
`lineHeight
` | chuỗi | Chiều cao dòng mặc định. Ví dụ.

"1.5"

,

"1.65"

. |
|
`letterSpacing
` | chuỗi | Khoảng cách chữ mặc định. Ví dụ.

"0"

,

"0.01em"

,

"-0.01em"

. |

`YAML
typography:
fontSans: '"Orbitron", "Eurostile", "Impact", sans-serif'
fontMono: '"Share Tech Mono", ui-monospace, monospace'
fontDisplay: '"Orbitron", "Eurostile", sans-serif'
fontUrl: "https://fonts.GoogleAPIs.com/css2?family=Orbitron:wght@400;500;600;700&family=Share+Tech+Mono&display=swap"
baseSize: "14px"
lineHeight: "1.5"
letterSpacing: "0.04em"

`

#### Bố cục

| Chìa khóa | Giá trị | Mô tả |
|------|--------|-------------|
|
`radius
` | bất kỳ độ dài CSS nào (

"0"

,

"0.25rem"

,

"0.5rem"

,

"1rem"

, ...) | Mã thông báo bán kính góc. Ánh xạ tới

`
--radius
` và xếp tầng vào

--radius-sm/md/lg/xl

- mọi phần tử tròn sẽ dịch chuyển cùng nhau. |
|
`density
` |
`compact
` \|
`comfortable
` \|
`spacious
` | Hệ số khoảng cách được áp dụng dưới dạng biến thể CSS

--spacing-mul

.
`compact = 0.85×

,
`comfortable = 1.0×
` (mặc định),
`spacious = 1.2×

. Tăng khoảng cách cơ sở của Tailwind, do đó, khoảng cách, khoảng trống và khoảng cách giữa các tiện ích đều thay đổi theo tỷ lệ. |

``` yaml
layout:
radius: "0"
density: compact

`

### Các biến thể của bố cục``layoutVariant
` chọn bố cục vỏ tổng thể. Mặc định là

"standard"
` khi vắng mặt.

| Biến thể | Hành vi |
|----------|----------|
|
`standard
` | Cột đơn, chiều rộng tối đa 1600px (mặc định). |
|
`cockpit
` | Đường ray bên trái (260px) + nội dung chính. Được phổ biến bởi các plugin thông qua khe
`sidebar

- xem [Shell slots](#shell-slots). Nếu không có plugin, đường ray sẽ hiển thị phần giữ chỗ. |
|
`tiled
` | Giảm giới hạn chiều rộng tối đa để các trang có thể sử dụng toàn bộ chiều rộng khung nhìn. |

`YAML
layoutVariant: cockpit

`
``Biến thể hiện tại được hiển thị dưới dạng
`document.documentElement.dataset.layoutVariant

, vì vậy CSS thô trong
`customCSS
` có thể nhắm mục tiêu nó thông qua

:root[data-layout-variant="cockpit"] ...

.

### Nội dung chủ đề (hình ảnh dưới dạng CSS)

Gửi URL tác phẩm nghệ thuật theo chủ đề. Mỗi vị trí được đặt tên sẽ trở thành một biến CSS (

`
--theme-asset-&lt;name>

) mà shell tích hợp và bất kỳ plugin nào đều có thể đọc được. Khe
`bg
` được tự động nối vào phông nền; các khe cắm khác hướng về phía plugin.

``` yaml
assets:
bg: "https://example.com/hero-bg.jpg" # auto-wired into &lt;Backdrop />
hero: "/my-images/strike-freedom.png" # for plugin sidebars
crest: "/my-images/crest.svg" # for header-left plugins
logo: "/my-images/logo.png"
sidebar: "/my-images/rail.png"
header: "/my-images/header-art.png"
custom:
scanLines: "/my-images/scanlines.png" # → --theme-asset-custom-scanLines

`
``Giá trị chấp nhận:
- URL trần — được gói tự động trong
`url(...)

.

- Các biểu thức
`url(...)

,
`linear-gradient(...)

,
`radial-gradient(...)
` được bao bọc sẵn — được sử dụng nguyên trạng.
-

"none"
` — chọn không tham gia rõ ràng.

Mọi nội dung cũng được phát ra dưới dạng

--theme-asset-&lt;name>-raw
` (URL chưa được gói), trong trường hợp plugin cần chuyển nó tới

<img src>
` thay vì
`background-image

.

Các plugin đọc chúng bằng CSS hoặc JS đơn giản:

`JavaScript
// In a plugin slot
const hero = getComputedStyle(document.documentElement)
.getPropertyValue("--theme-asset-hero").trim();

`

### Ghi đè thành phần chrome``componentStyles
` sắp xếp lại các thành phần shell riêng lẻ mà không cần ghi bộ chọn CSS. Các mục nhập của mỗi nhóm trở thành các vars CSS (

--component-&lt;bucket>-&lt;kebab-property>

) mà các thành phần chia sẻ của shell đọc được. Vì vậy, phần ghi đè
`card:
` áp dụng cho mọi

&lt;Card>

,
`header:
` trên thanh ứng dụng, v.v.

``` yaml
componentStyles:
card:
CLIpPath: "polygon(12px 0, 100% 0, 100% calc(100% - 12px), calc(100% - 12px) 100%, 0 100%, 0 12px)"
background: "linear-gradient(180deg, rgba(10, 22, 52, 0.85), rgba(5, 9, 26, 0.92))"
boxShadow: "inset 0 0 0 1px rgba(64, 200, 255, 0.28)"
header:
background: "linear-gradient(180deg, rgba(16, 32, 72, 0.95), rgba(5, 9, 26, 0.9))"
tab:
CLIpPath: "polygon(6px 0, 100% 0, calc(100% - 6px) 100%, 0 100%)"
sidebar: \{}
backdrop: \{}
footer: \{}
progress: \{}
badge: \{}
page: \{}

`
``Các nhóm được hỗ trợ:
`card

,
`header

,
`footer

,
`sidebar

,
`tab

,
`progress

,
`badge

,
`backdrop

,
`page

.

Tên thuộc tính sử dụng CamelCase (
`CLIpPath

) và được phát ra dưới dạng kebab (
`CLIp-path

). Giá trị là các chuỗi CSS đơn giản — mọi thứ CSS đều chấp nhận (
`CLIp-path

,
`border-image

,
`background

,
`box-shadow

,
`animation

, ...).

### Ghi đè màu

Hầu hết các chủ đề sẽ không cần điều này — bảng màu 3 lớp lấy ra mọi mã thông báo shadcn. Sử dụng
`colorOverrides
` khi bạn muốn có một điểm nhấn cụ thể mà nguồn gốc sẽ không tạo ra (màu đỏ hủy diệt nhẹ nhàng hơn cho chủ đề màu phấn, màu xanh lá cây thành công cụ thể cho thương hiệu).

`YAML
colorOverrides:
primary: "#ffce3a"
primaryForeground: "#05091a"
accent: "#3fd3ff"
ring: "#3fd3ff"
destructive: "#ff3a5e"
border: "rgba(64, 200, 255, 0.28)"

`
`Các khóa được hỗ trợ:
`card

,
`cardForeground

,
`popover

,
`popoverForeground

,
`primary

,
`primaryForeground

,
`secondary

,
`secondaryForeground

,
`muted

,
`mutedForeground

,
`accent

,
`accentForeground

,
`destructive

,
`destructiveForeground

,
`success

,
`warning

,
`border

,
`input

,
`ring

.

Mỗi khóa ánh xạ 1:1 tới biến CSS

`
--color-&lt;kebab>
` (ví dụ:
`primaryForeground
`

--color-primary-foreground

). Bất kỳ khóa nào được đặt ở đây sẽ giành chiến thắng trong tầng bảng màu chỉ dành cho chủ đề đang hoạt động — việc chuyển sang chủ đề khác sẽ xóa phần ghi đè.

###
`customCSS
` thô

Đối với chrome cấp bộ chọn mà
`componentStyles
` không thể biểu thị — phần tử giả, hoạt ảnh, truy vấn phương tiện, ghi đè theo phạm vi chủ đề — thả CSS thô vào
`customCSS

:

``` yaml
customCSS: |
/* Scanline overlay — only visible when cockpit variant is active. */
:root[data-layout-variant="cockpit"] body::before {
content: "";
position: fixed;
inset: 0;
pointer-events: none;
z-index: 100;
background: repeating-linear-gradient(to bottom,
transparent 0px, transparent 2px,
rgba(64, 200, 255, 0.035) 3px, rgba(64, 200, 255, 0.035) 4px);
mix-blend-mode: screen;
}

`
``CSS được đưa vào dưới dạng một thẻ

&lt;style data-Hermes-theme-css>
` có phạm vi duy nhất khi áp dụng chủ đề và được dọn dẹp khi chuyển đổi chủ đề. **Giới hạn ở mức 32 KiB mỗi chủ đề.**

### Chủ đề tích hợp

Mỗi tính năng tích hợp đều có bảng màu, kiểu chữ và bố cục riêng — việc chuyển đổi sẽ tạo ra những thay đổi rõ ràng ngoài màu sắc.

| Chủ đề | Bảng màu | Kiểu chữ | Bố cục |
|-------|----------|-------------|--------|
| **Hermes Teal** (
`default

) | Màu xanh mòng két + kem | Ngăn xếp hệ thống, 15px | Bán kính 0,5rem, thoải mái |
| **Hermes Teal (Lớn)** (
`default-large

) | Giống như mặc định | Ngăn xếp hệ thống, 18px, chiều cao dòng 1,65 | Bán kính 0,5rem, rộng rãi |
| **Nửa đêm** (
`midnight

) | Màu xanh tím đậm | Inter + JetBrains Mono, 14px | Bán kính 0,75rem, thoải mái |
| **Ember** (
`ember

) | Đỏ thẫm + đồng ấm áp | Quang phổ (serif) + IBM Plex Mono, 15px | Bán kính 0,25rem, thoải mái |
| **Mono** (
`mono

) | Thang độ xám | IBM Plex Sans + IBM Plex Mono, 13px | Bán kính 0, nhỏ gọn |
| **Cyberpunk** (
`cyberpunk

) | Màu xanh neon trên nền đen | Chia sẻ Tech Mono ở mọi nơi, 14px | Bán kính 0, nhỏ gọn |
| **Rosé** (
`rose

) | Hồng + ngà | Fraunces (serif) + DM Mono, 16px | Bán kính 1rem, rộng rãi |

Các chủ đề tham chiếu đến Google Fonts (tất cả ngoại trừ Hermes Teal) tải biểu định kiểu theo yêu cầu — lần đầu tiên bạn chuyển sang chúng, thẻ

&lt;link>
` sẽ được đưa vào

&lt;head>

.

### Tham khảo đầy đủ chủ đề YAML

Mọi núm trong một tệp — sao chép và cắt bớt những gì bạn không cần:

`YAML

# ~/.Hermes/dashboard-themes/ocean.YAML
name: ocean
label: Ocean Deep
description: Deep sea blues with coral accents

# 3-layer palette (accepts \{hex, alpha} or bare hex)
palette:
background:
hex: "#0a1628"
alpha: 1.0
midground:
hex: "#a8d0ff"
alpha: 1.0
foreground:
hex: "#ffffff"
alpha: 0.0
warmGlow: "rgba(255, 107, 107, 0.35)"
noiseOpacity: 0.7`typography:
fontSans: "Poppins, system-ui, sans-serif"
fontMono: "Fira Code, ui-monospace, monospace"
fontDisplay: "Poppins, system-ui, sans-serif" # optional
fontUrl: "https://fonts.GoogleAPIs.com/css2?family=Poppins:wght@400;500;600&family=Fira+Code:wght@400;500&display=swap"
baseSize: "15px"
lineHeight: "1.6"
letterSpacing: "-0.003em"`layout:
radius: "0.75rem"
density: comfortable`layoutVariant: standard # standard | cockpit | tiled`assets:
bg: "https://example.com/ocean-bg.jpg"
hero: "/my-images/kraken.png"
crest: "/my-images/anchor.svg"
logo: "/my-images/logo.png"
custom:
pattern: "/my-images/waves.svg"`componentStyles:
card:
boxShadow: "inset 0 0 0 1px rgba(168, 208, 255, 0.18)"
header:
background: "linear-gradient(180deg, rgba(10, 22, 40, 0.95), rgba(5, 9, 26, 0.9))"`colorOverrides:
destructive: "#ff6b6b"
ring: "#ff6b6b"`customCSS: |
/* Any additional selector-level tweaks */

`
``Làm mới bảng điều khiển sau khi tạo tệp. Chuyển chủ đề trực tiếp từ thanh tiêu đề — nhấp vào biểu tượng bảng màu. Lựa chọn vẫn tiếp tục đối với
`config.yaml
` trong
`dashboard.theme
` và được khôi phục khi tải lại.

---

## Plugin

Plugin bảng điều khiển là một thư mục có
`manifest.JSON

, gói JS dựng sẵn và tùy chọn là tệp CSS và tệp Python có các tuyến FastAPI. Các plugin nằm cạnh các plugin Hermes khác trong

~/.Hermes/plugins/&lt;name>/
` — tiện ích mở rộng bảng thông tin là thư mục con
`dashboard/
` bên trong thư mục plugin đó, vì vậy một plugin có thể mở rộng cả CLI/gateway và bảng thông tin từ một lần cài đặt.

Các plugin không gói các thành phần React hoặc UI. Họ sử dụng **SDK plugin** được cung cấp trên
`window.__Hermes_PLUGIN_SDK__

. Điều này giữ cho các gói plugin có kích thước nhỏ (thường là vài KB) và tránh xung đột phiên bản.

### Bắt đầu nhanh — plugin đầu tiên của bạn

Tạo cấu trúc thư mục:

``` bash
mkdir -p ~/.Hermes/plugins/my-plugin/dashboard/dist

`
``Viết bảng kê khai:

`JSON
// ~/.Hermes/plugins/my-plugin/dashboard/manifest.JSON
{
"name": "my-plugin",
"label": "My Plugin",
"icon": "Sparkles",
"version": "1.0.0",
"tab": {
"path": "/my-plugin",
"position": "after:skills"
},
"entry": "dist/index.js"
}

`
``Viết gói JS (IIFE đơn giản - không cần bước xây dựng):

`JavaScript
// ~/.Hermes/plugins/my-plugin/dashboard/dist/index.js
(function () {
"use strict";`const SDK = window.__Hermes_PLUGIN_SDK__;
const { React } = SDK;
const { Card, CardHeader, CardTitle, CardContent } = SDK.components;`function MyPage() {
return React.createElement(Card, null,
React.createElement(CardHeader, null,
React.createElement(CardTitle, null, "My Plugin"),
),
React.createElement(CardContent, null,
React.createElement("p", { className: "text-sm text-muted-foreground" },
"Hello from my custom dashboard tab.",
),
),
);
}

window.__Hermes_PLUGINS__.register("my-plugin", MyPage);
})();

`
``Làm mới trang tổng quan — tab của bạn sẽ xuất hiện trong thanh điều hướng, sau **Kỹ năng**.

:::tip[Skip React.createElement]
Nếu bạn thích JSX, hãy sử dụng bất kỳ trình đóng gói nào (esbuild, Vite, rollup) với React làm đầu ra bên ngoài và IIFE. Yêu cầu khó khăn duy nhất là tệp cuối cùng phải là một tệp JS duy nhất có thể tải qua

&lt;script>

. React không bao giờ được đóng gói; nó đến từ
`SDK.React

.

:::

### Bố cục thư mục

`
~/.Hermes/plugins/my-plugin/
├── plugin.YAML # optional — existing CLI/gateway plugin manifest
├── __init__.py # optional — existing CLI/gateway hooks
└── dashboard/ # dashboard extension
├── manifest.JSON # required — tab config, icon, entry point
├── dist/
│ ├── index.js # required — pre-built JS bundle (IIFE)
│ └── style.css # optional — custom CSS
└── plugin_API.py # optional — backend API routes (FastAPI)

`
``Một thư mục plugin có thể chứa ba phần mở rộng trực giao:
-
`plugin.YAML

+
`__init__.py
` — plugin CLI/cổng ([see plugins page](./plugins)).
-
`dashboard/manifest.JSON

+
`dashboard/dist/index.js
` — plugin giao diện người dùng bảng điều khiển.
-
`dashboard/plugin_API.py
` — các tuyến đường phụ trợ trên bảng điều khiển.

Không ai trong số họ được yêu cầu; chỉ bao gồm các lớp bạn cần.

### Tài liệu tham khảo kê khai

``` json
{
"name": "my-plugin",
"label": "My Plugin",
"description": "What this plugin does",
"icon": "Sparkles",
"version": "1.0.0",
"tab": {
"path": "/my-plugin",
"position": "after:skills",
"override": "/",
"hidden": false
},
"slots": ["sidebar", "header-left"],
"entry": "dist/index.js",
"css": "dist/style.css",
"API": "plugin_API.py"
}

`

| Lĩnh vực | Bắt buộc | Mô tả |
|-------|----------|-------------|
|
`name
` || Mã định danh plugin duy nhất. Chữ thường, dấu gạch nối ok. Được sử dụng trong URL và đăng ký. |
|
`label
` || Tên hiển thị được hiển thị trong tab điều hướng. |
|
`description
` | Không | Mô tả ngắn (hiển thị trên giao diện quản trị trang tổng quan). |
|
`icon
` | Không | Tên biểu tượng Lucide. Mặc định là
`Puzzle

. Những cái tên không xác định quay trở lại
`Puzzle

. |
|
`version
` | Không | Chuỗi ngữ nghĩa. Mặc định là
`0.0.0

. |
|
`tab.path
` || Đường dẫn URL cho tab (ví dụ:

/my-plugin

). |
|
`tab.position
` | Không | Vị trí chèn tab.

"end"
` (mặc định),

"after:<path>"
` hoặc

"before:<path>"
` — giá trị sau dấu hai chấm là **đoạn đường dẫn** của tab mục tiêu (không có dấu gạch chéo ở đầu). Ví dụ:

"after:skills"

,

"before:config"

. |
|
`tab.override
` | Không | Đặt thành đường dẫn lộ trình tích hợp (

"/"

,

"/sessions"

,

"/config"

, ...) để **thay thế** trang đó thay vì thêm tab mới. Xem [Replacing built-in pages](#replacing-built-in-pages-taboverride). |
|
`tab.hidden
` | Không | Khi đúng, hãy đăng ký thành phần và bất kỳ vị trí nào mà không cần thêm tab vào điều hướng. Được sử dụng bởi các plugin chỉ có khe cắm. Xem [Slot-only plugins](#slot-only-plugins-tabhidden). |
|
`slots
` | Không | Các khe shell được đặt tên mà plugin này đưa vào. **Chỉ hỗ trợ tài liệu** — quá trình đăng ký thực tế diễn ra từ gói JS thông qua
`registerSlot()

. Các vị trí danh sách ở đây làm cho bề mặt khám phá có nhiều thông tin hơn. |
|
`entry
` || Đường dẫn đến gói JS liên quan đến
`dashboard/

. Mặc định là
`dist/index.js

. |
|
`css
` | Không | Đường dẫn đến tệp CSS để chèn dưới dạng thẻ

&lt;link>

. |
|
`API
` | Không | Đường dẫn đến tệp Python với các tuyến FastAPI. Được gắn tại

/API/plugins/&lt;name>/

. |

#### Biểu tượng có sẵn

Các plugin sử dụng tên biểu tượng Lucide. Trang tổng quan ánh xạ những tên này - những tên không xác định âm thầm quay trở lại
`Puzzle

.

Hiện được ánh xạ:
`Activity

,
`BarChart3

,
`Clock

,
`Code

,
`Database

,
`Eye

,
`FileText

,
`Globe

,
`Heart

,
`KeyRound

,
`MessageSquare

,
`Package

,
`Puzzle

,
`Settings

,
`Shield

,
`Sparkles

,
`Star

,
`Terminal

,
`Wrench

,
`Zap

.

Cần một biểu tượng khác? Mở PR cho
`web/src/App.tsx
` của
`ICON_MAP

- thay đổi phụ gia thuần túy.

### SDK plugin

Mọi thứ mà một plugin cần đều có trên
`window.__Hermes_PLUGIN_SDK__

. Các plugin không bao giờ nên nhập React trực tiếp.

`JavaScript
const SDK = window.__Hermes_PLUGIN_SDK__;`// React + hooks
SDK.React // the React instance
SDK.hooks.useState
SDK.hooks.useEffect
SDK.hooks.useCallback
SDK.hooks.useMemo
SDK.hooks.useRef
SDK.hooks.useContext
SDK.hooks.createContext`// UI components (shadcn/ui primitives)
SDK.components.Card
SDK.components.CardHeader
SDK.components.CardTitle
SDK.components.CardContent
SDK.components.Badge
SDK.components.Button
SDK.components.Input
SDK.components.Label
SDK.components.Select
SDK.components.SelectOption
SDK.components.Separator
SDK.components.Tabs
SDK.components.TabsList
SDK.components.TabsTrigger
SDK.components.PluginSlot // render a named slot (useful for nested plugin UIs)`// Hermes API CLIent + raw fetcher
SDK.API // typed CLIent — getStatus, getSessions, getConfig, ...
SDK.fetchJSON // raw fetch for custom endpoints (plugin-registered routes)`// Utilities
SDK.utils.cn // Tailwind class merger (clsx + twMerge)
SDK.utils.timeAgo // "5m ago" from unix timestamp
SDK.utils.isoTimeAgo // "5m ago" from ISO string`// Hooks
SDK.useI18n // i18n hook for multi-language plugins

`

#### Gọi phần phụ trợ của plugin của bạn

`JavaScript
SDK.fetchJSON("/API/plugins/my-plugin/data")
.then((data) => console.log(data))
.catch((err) => console.error("API call failed:", err));

`
```fetchJSON
` chèn mã thông báo xác thực phiên, hiển thị các lỗi dưới dạng ngoại lệ được gửi và tự động phân tích cú pháp JSON.

#### Gọi các điểm cuối Hermes tích hợp

`JavaScript
// Agent status
SDK.API.getStatus().then((s) => console.log("Version:", s.version));`// Recent sessions
SDK.API.getSessions(10).then((resp) => console.log(resp.sessions.length));

`
``Xem [Web Dashboard → REST API](./web-dashboard#rest-API) để biết danh sách đầy đủ.

### Khe vỏ

Các vị trí cho phép plugin đưa các thành phần vào các vị trí được đặt tên của vỏ ứng dụng — thanh bên buồng lái, đầu trang, chân trang, lớp phủ — mà không yêu cầu toàn bộ tab. Nhiều plugin có thể nằm trong cùng một vị trí; chúng hiển thị xếp chồng lên nhau theo thứ tự đăng ký.

Đăng ký từ bên trong gói plugin:

`JavaScript
window.__Hermes_PLUGINS__.registerSlot("my-plugin", "sidebar", MySidebar);
window.__Hermes_PLUGINS__.registerSlot("my-plugin", "header-left", MyCrest);

`

#### Danh mục máy đánh bạc`**Các khe toàn Shell** (hiển thị ở mọi nơi trong ứng dụng chrome):

| Khe | Vị trí |
|------|----------|
|
`backdrop
` | Bên trong ngăn xếp lớp

&lt;Backdrop /

, phía trên lớp nhiễu. |
|
`header-left
` | Trước thương hiệu Hermes ở thanh trên cùng. |
|
`header-right
` | Trước bộ chuyển đổi chủ đề/ngôn ngữ ở thanh trên cùng. |
|
`header-banner
` | Dải có chiều rộng đầy đủ bên dưới điều hướng. |
|
`sidebar
` | Thanh ray bên trong buồng lái — **chỉ được hiển thị khi
`layoutVariant === "cockpit"

`
**. |
|
`pre-main
` | Phía trên ổ cắm tuyến đường (bên trong

&lt;main

). |
|
`post-main
` | Bên dưới ổ cắm tuyến đường (bên trong

&lt;main

). |
|
`footer-left
` | Nội dung ô chân trang (thay thế mặc định). |
|
`footer-right
` | Nội dung ô chân trang (thay thế mặc định). |
|
`overlay
` | Lớp vị trí cố định trên mọi thứ khác. Hữu ích cho chrome (dòng quét, họa tiết)
`customCSS
` không thể đạt được một mình. |**Vị trí trong phạm vi trang** (chỉ hiển thị trên trang tích hợp có tên — sử dụng các vị trí này để đưa tiện ích, thẻ hoặc thanh công cụ vào trang hiện có mà không ghi đè toàn bộ tuyến):
| Khe | Nơi nó hiển thị |
|------|-------------------|
|
`sessions:top
` /
`sessions:bottom
` | Đầu/cuối trang

/sessions

. |
|
`analytics:top
` /
`analytics:bottom
` | Đầu/cuối trang

/analytics

. |
|
`logs:top
` /
`logs:bottom
` | Trên cùng (phía trên thanh công cụ bộ lọc) / dưới cùng (bên dưới trình xem nhật ký) của

/logs

. |
|
`cron:top
` /
`cron:bottom
` | Đầu/cuối trang

/cron

. |
|
`skills:top
` /
`skills:bottom
` | Trên/dưới trang

/skills

. |
|
`config:top
` /
`config:bottom
` | Trên/dưới trang

/config

. |
|
`env:top
` /
`env:bottom
` | Trên/dưới trang

/env
` (Keys). |
|
`docs:top
` /
`docs:bottom
` | Trên cùng (phía trên iframe) / dưới cùng của

/docs

. |
|
`chat:top
` /
`chat:bottom
` | Trên/dưới của

/chat
` (chỉ hoạt động khi bật tính năng trò chuyện nhúng). |

Ví dụ - thêm thẻ biểu ngữ vào đầu trang Phiên:

`JavaScript
function PinnedSessionsBanner() &#123;
return React.createElement(Card, null,
React.createElement(CardContent, &#123; className: "py-2 text-xs" &#125;,
"Pinned note injected by my-plugin"),
);
&#125;`window.__Hermes_PLUGINS__.registerSlot("my-plugin", "sessions:top", PinnedSessionsBanner);

`
``Kết hợp các vị trí trong phạm vi trang với
`tab.hidden: true
` nếu plugin của bạn chỉ tăng cường các trang hiện có và không cần tab thanh bên của riêng nó.

Shell chỉ hiển thị

&lt;PluginSlot name="..." /
` cho các khe ở trên. Các tên bổ sung được cơ quan đăng ký chấp nhận cho các giao diện người dùng plugin lồng nhau — một plugin có thể hiển thị các vị trí riêng của nó thông qua
`SDK.components.PluginSlot

.`####Đăng ký lại và HMR

Nếu cùng một cặp

(plugin, slot)
` được đăng ký hai lần, lệnh gọi sau sẽ thay thế lệnh gọi trước đó - điều này khớp với cách React HMR mong đợi hoạt động gắn lại plugin.

### Thay thế các trang cài sẵn (
`tab.override

)

Đặt
`tab.override
` thành đường dẫn lộ trình tích hợp sẽ khiến thành phần của plugin thay thế trang đó thay vì thêm tab mới. Hữu ích khi một chủ đề muốn có một trang chủ tùy chỉnh (

/

) nhưng muốn giữ nguyên phần còn lại của trang tổng quan.

``` json
&#123;
"name": "my-home",
"label": "Home",
"tab": &#123;
"path": "/my-home",
"override": "/",
"position": "end"
&#125;,
"entry": "dist/index.js"
&#125;

`
``Với bộ
`override

:
- Thành phần trang gốc tại

/
` bị xóa khỏi bộ định tuyến.

- Thay vào đó, plugin của bạn hiển thị tại

/

.
- Không có tab điều hướng nào được thêm vào cho
`tab.path
` (ghi đè là điểm).

Chỉ một plugin có thể ghi đè một đường dẫn nhất định. Nếu hai plugin yêu cầu ghi đè giống nhau, thì plugin đầu tiên sẽ thắng và plugin thứ hai sẽ bị bỏ qua với cảnh báo ở chế độ nhà phát triển.

Nếu bạn chỉ cần thêm thẻ hoặc thanh công cụ vào một trang hiện có mà không tiếp quản nó, hãy sử dụng [page-scoped slots](#augmenting-built-in-pages-page-scoped-slots) thay thế.

### Tăng cường các trang tích hợp (các vị trí trong phạm vi trang)

Việc thay thế hoàn toàn thông qua
`tab.override
` rất nặng — plugin của bạn hiện sở hữu toàn bộ trang, bao gồm mọi bản cập nhật trong tương lai mà chúng tôi gửi tới nó. Hầu hết bạn chỉ muốn thêm biểu ngữ, thẻ hoặc thanh công cụ vào trang hiện có. Đó chính là mục đích của **các vị trí trong phạm vi trang**.

Mỗi trang tích hợp đều hiển thị các vị trí

&lt;page:top
`

&lt;page:bottom
` được hiển thị ở đầu và cuối khu vực nội dung của nó. Plugin của bạn sẽ điền một plugin bằng cách gọi
`registerSlot()

- trang tích hợp sẵn tiếp tục hoạt động bình thường và thành phần của bạn hiển thị cùng với nó.

Các khe có sẵn:
`sessions:*

,
`analytics:*

,
`logs:*

,
`cron:*

,
`skills:*

,
`config:*

,
`env:*

,
`docs:*

,
`chat:*
` (mỗi khe có

:top
`

:bottom

). Xem danh mục đầy đủ trong [Shell slots → Slot catalogue](#slot-catalogue).

Ví dụ tối thiểu - ghim biểu ngữ lên đầu trang Phiên:

``` json
// ~/.Hermes/plugins/session-notes/dashboard/manifest.JSON
&#123;
"name": "session-notes",
"label": "Session Notes",
"tab": &#123; "path": "/session-notes", "hidden": true &#125;,
"slots": ["sessions:top"],
"entry": "dist/index.js"
&#125;

`

`
`JavaScript
// ~/.Hermes/plugins/session-notes/dashboard/dist/index.js
(function () &#123;
const SDK = window.__Hermes_PLUGIN_SDK__;
const &#123; React &#125; = SDK;
const &#123; Card, CardContent &#125; = SDK.components;`function Banner() &#123;
return React.createElement(Card, null,
React.createElement(CardContent, &#123; className: "py-2 text-xs" &#125;,
"Remember to label important sessions before archiving."),
);
&#125;`// Placeholder for the hidden tab.
window.__Hermes_PLUGINS__.register("session-notes", function () &#123; return null; &#125;);`// The real work.
window.__Hermes_PLUGINS__.registerSlot("session-notes", "sessions:top", Banner);
&#125;)();

`
``Những điểm chính:
-
`tab.hidden: true
` giữ plugin ngoài thanh bên — nó không có trang độc lập.

- Trường bảng kê khai
`slots
` chỉ mang tính chất tài liệu. Ràng buộc thực tế xảy ra trong gói JS thông qua
`registerSlot()

.
- Nhiều plugin có thể yêu cầu cùng một vị trí trong phạm vi trang. Họ kết xuất xếp chồng lên nhau theo thứ tự đăng ký.
- Không có dấu chân khi không đăng ký plugin: trang tích hợp hiển thị chính xác như trước.

Một plugin tham chiếu (
`example-dashboard
` trong [XPROTECTX309XPROTECTX](https://GitHub.com/NousResearch/Hermes-example-plugins/tree/main/example-dashboard)) gửi bản demo trực tiếp đưa biểu ngữ vào
`sessions:top

- cài đặt nó để xem mẫu từ đầu đến cuối.

### Các plugin chỉ có khe cắm (
`tab.hidden

)Khi
`tab.hidden: true

, plugin đăng ký thành phần của nó (đối với các lượt truy cập URL trực tiếp) và bất kỳ vị trí nào, nhưng không bao giờ thêm tab vào điều hướng. Được sử dụng bởi các plugin chỉ tồn tại để chèn vào các vị trí - đỉnh tiêu đề, HUD thanh bên, lớp phủ.

``` json
&#123;
"name": "header-crest",
"label": "Header Crest",
"tab": &#123;
"path": "/header-crest",
"position": "end",
"hidden": true
&#125;,
"slots": ["header-left"],
"entry": "dist/index.js"
&#125;

`
``Gói vẫn gọi
`register()
` với thành phần giữ chỗ (cách thực hành tốt trong trường hợp ai đó truy cập trực tiếp vào URL) và sau đó
`registerSlot()
` để thực hiện công việc thực tế.

### Các tuyến API phụ trợ

Các plugin có thể đăng ký các tuyến FastAPI bằng cách đặt
`API
` trong tệp kê khai. Tạo tệp và xuất
`router

:

`Python

# ~/.Hermes/plugins/my-plugin/dashboard/plugin_API.py
from fastAPI import APIRouter`router = APIRouter()`@router.get("/data")
async def get_data():
return \&#123;"items": ["one", "two", "three"]&#125;`@router.post("/action")
async def do_action(body: dict):
return \&#123;"ok": True, "received": body&#125;

`
``Các tuyến đường được gắn trong

/API/plugins/&lt;name/

, do đó, ở trên trở thành:
-
`GET /API/plugins/my-plugin/data

-
`POST /API/plugins/my-plugin/action
``Các tuyến API plugin bỏ qua xác thực mã thông báo phiên vì máy chủ bảng điều khiển liên kết với localhost theo mặc định. **Không hiển thị trang tổng quan trên giao diện công cộng với

--host 0.0.0.0
` nếu bạn chạy các plugin không đáng tin cậy** — các tuyến đường của chúng cũng có thể truy cập được.

#### Truy cập nội bộ của Hermes

Các tuyến phụ trợ chạy bên trong quy trình bảng điều khiển, vì vậy chúng có thể nhập trực tiếp từ cơ sở mã Hermes-agent:

``` python
from fastAPI import APIRouter
from Hermes_state import SessionDB
from Hermes_CLI.config import load_config`router = APIRouter()`@router.get("/session-count")
async def session_count():
db = SessionDB()
try:
count = len(db.list_sessions(limit=9999))
return \&#123;"count": count&#125;
finally:
db.close()`@router.get("/config-snapshot")
async def config_snapshot():
cfg = load_config()
return \&#123;"model": cfg.get("model", \&#123;&#125;)&#125;

`

### CSS tùy chỉnh cho mỗi plugin

Nếu plugin của bạn cần các kiểu ngoài lớp Tailwind và
`style=
` nội tuyến, hãy thêm tệp CSS và tham chiếu tệp đó trong tệp kê khai:

`JSON
&#123;
"css": "dist/style.css"
&#125;

`
``Tệp được chèn dưới dạng thẻ

&lt;link
` khi tải plugin. Sử dụng tên lớp cụ thể để tránh xung đột với kiểu của trang tổng quan và tham chiếu các biến CSS của trang tổng quan để luôn nhận biết chủ đề:

`css
/* dist/style.css */
.my-plugin-chart &#123;
border: 1px solid var(--color-border);
background: var(--color-card);
color: var(--color-card-foreground);
padding: 1rem;
&#125;
.my-plugin-chart:hover &#123;
border-color: var(--color-ring);
&#125;

`
``Trang tổng quan hiển thị mọi mã thông báo shadcn dưới dạng

`
--color-*
` cùng với các tính năng bổ sung của chủ đề (

--theme-asset-*

,

--component-&lt;bucket-*

,

--radius

,

--spacing-mul

). Hãy tham khảo những thứ đó và plugin của bạn sẽ tự động thay đổi giao diện với chủ đề đang hoạt động.

### Khám phá và tải lại plugin

Bảng điều khiển quét ba thư mục để tìm
`dashboard/manifest.JSON

:

| Ưu tiên | Thư mục | Nhãn nguồn |
|----------|-------------|--------------|
| 1 (thắng khi xung đột) |

~/.Hermes/plugins/&lt;name/dashboard/
` |
`user
` |
| 2 |

&lt;repo/plugins/memory/&lt;name/dashboard/
` |
`bundled
` |
| 2 |

&lt;repo/plugins/&lt;name/dashboard/
` |
`bundled
` |
| 3 |

./.Hermes/plugins/&lt;name/dashboard/
` |
`project

- chỉ khi
`Hermes_ENABLE_PROJECT_PLUGINS
` được đặt |

Kết quả khám phá được lưu vào bộ đệm cho mỗi quy trình trên trang tổng quan. Sau khi thêm một plugin mới, hãy:

``` bash

# Force a rescan without restart
curl http://127.0.0.1:9119/API/dashboard/plugins/rescan

`
``…hoặc khởi động lại
`Hermes dashboard

.

#### Vòng đời tải plugin
1. Tải bảng điều khiển.
`main.tsx
` hiển thị SDK trên
`window.__Hermes_PLUGIN_SDK__
` và sổ đăng ký trên
`window.__Hermes_PLUGINS__

.
2.
`App.tsx
` gọi
`usePlugins()
` → tìm nạp
`GET /API/dashboard/plugins

.
3. Đối với mỗi tệp kê khai: CSS

&lt;link
` được chèn (nếu được khai báo), sau đó thẻ

&lt;script
` sẽ tải gói JS.
4. IIFE của plugin chạy và gọi
`window.__Hermes_PLUGINS__.register(name, Component)
` — và

.registerSlot(name, slot, Component)
` tùy chọn cho mỗi vị trí.
5. Bảng thông tin giải quyết thành phần đã đăng ký dựa trên bảng kê khai, thêm tab vào điều hướng (trừ khi
`hidden

) và gắn thành phần đó làm tuyến đường.

Các plugin có tối đa **2 giây** sau khi tải tập lệnh để gọi
`register()

. Sau đó, bảng điều khiển ngừng chờ và hoàn tất kết xuất ban đầu. Nếu sau này plugin đăng ký, nó vẫn xuất hiện - điều hướng đang hoạt động.

Nếu tập lệnh của plugin không tải được (404, lỗi cú pháp, ngoại lệ trong IIFE), bảng thông tin sẽ ghi cảnh báo vào bảng điều khiển trình duyệt và tiếp tục mà không có cảnh báo đó.

---

## Bản demo chủ đề + plugin kết hợp

Plugin [XPROTECTX351XPROTECTX](https://GitHub.com/NousResearch/Hermes-example-plugins/tree/main/strike-freedom-cockpit) (repo đi kèm
`Hermes-example-plugins

) là bản demo reskin hoàn chỉnh. Nó kết hợp chủ đề YAML với một plugin chỉ có khe cắm để tạo ra HUD kiểu buồng lái mà không cần chia bảng điều khiển.

**Nó thể hiện điều gì:**
- Một chủ đề đầy đủ sử dụng bảng màu, kiểu chữ,
`fontUrl

,
`layoutVariant: cockpit

,
`assets

,
`componentStyles
` (góc thẻ có khía, nền chuyển màu),
`colorOverrides
`
`customCSS
` (lớp phủ đường quét).
- Một plugin chỉ dành cho vị trí (
`tab.hidden: true

) đăng ký thành ba vị trí:
-
`sidebar
` — bảng MS-STATUS với các thanh đo từ xa trực tiếp được điều khiển bởi
`SDK.API.getStatus()

.
-
`header-left
` — huy hiệu phe phái ghi

--theme-asset-crest
` từ chủ đề đang hoạt động.
-
`footer-right
` — dòng giới thiệu tùy chỉnh thay thế dòng tổ chức mặc định.
- Plugin đọc tác phẩm nghệ thuật do chủ đề cung cấp thông qua các vars CSS, do đó, việc hoán đổi chủ đề sẽ thay đổi anh hùng/huy hiệu mà không thay đổi mã plugin.

**Cài đặt:**

`
``` bash
git clone https://GitHub.com/NousResearch/Hermes-example-plugins.git

# Theme
cp Hermes-example-plugins/strike-freedom-cockpit/theme/strike-freedom.YAML \
~/.Hermes/dashboard-themes/

# Plugin
cp -r Hermes-example-plugins/strike-freedom-cockpit ~/.Hermes/plugins/

`
``Mở bảng điều khiển, chọn **Strike Freedom** từ trình chuyển đổi chủ đề. Thanh bên buồng lái xuất hiện, biểu tượng hiển thị ở phần đầu trang, dòng tagline thay thế cho phần chân trang. Chuyển về **Hermes Teal** và plugin vẫn được cài đặt nhưng ẩn (khe
`sidebar
` chỉ hiển thị theo biến thể bố cục
`cockpit

).

Đọc nguồn plugin (
`strike-freedom-cockpit/dashboard/dist/index.js
` trong kho đi kèm) để biết cách nó đọc các biến thể CSS, bảo vệ khỏi các trang tổng quan cũ hơn mà không hỗ trợ vị trí và đăng ký ba vị trí từ một gói.

---

## Tham chiếu API

### Điểm cuối của chủ đề

| Điểm cuối | Phương pháp | Mô tả |
|----------|--------|-------------|
|

/API/dashboard/themes
` | NHẬN | Liệt kê các chủ đề có sẵn + tên hoạt động. Tích hợp trả về

\&#123;name, label, description&#125;

; chủ đề người dùng cũng bao gồm trường
`definition
` với đối tượng chủ đề được chuẩn hóa đầy đủ. |
|

/API/dashboard/theme
` | ĐƯA | Đặt chủ đề hoạt động. Thân máy:

\&#123;"name": "midnight"&#125;

. Vẫn tồn tại với
`config.yaml
` trong
`dashboard.theme

. |

### Điểm cuối của plugin

| Điểm cuối | Phương pháp | Mô tả |
|----------|--------|-------------|
|

/API/dashboard/plugins
` | NHẬN | Liệt kê các plugin được phát hiện (có bảng kê khai, trừ các trường nội bộ). |
|

/API/dashboard/plugins/rescan
` | NHẬN | Buộc quét lại các thư mục plugin mà không cần khởi động lại. |
|

/dashboard-plugins/&lt;name/&lt;path
` | NHẬN | Cung cấp nội dung tĩnh từ thư mục
`dashboard/
` của plugin. Truyền tải đường dẫn bị chặn. |
|

/API/plugins/&lt;name/*
` | * | Các tuyến phụ trợ đã đăng ký plugin. |

### SDK trên
`window

| Toàn cầu | Loại | Nhà cung cấp |
|--------|------|----------|
|
`window.__Hermes_PLUGIN_SDK__
` | đối tượng |

registry.ts

- Phản ứng, hook, thành phần giao diện người dùng, ứng dụng khách API, tiện ích. |
|
`window.__Hermes_PLUGINS__.register(name, Component)
` | chức năng | Đăng ký thành phần chính của plugin. |
|
`window.__Hermes_PLUGINS__.registerSlot(name, slot, Component)
` | chức năng | Đăng ký vào một khe shell có tên. |

---

## Khắc phục sự cố`**Chủ đề của tôi không xuất hiện trong bộ chọn.**
Kiểm tra xem tệp có ở định dạng

~/.Hermes/dashboard-themes/
` và kết thúc bằng

.YAML
` hoặc

.yml
` hay không. Làm mới trang. Chạy
`curl http://127.0.0.1:9119/API/dashboard/themes

- chủ đề của bạn sẽ phản hồi. Nếu YAML có lỗi phân tích cú pháp, bảng điều khiển sẽ ghi vào
`errors.log
` trong

~/.Hermes/logs/

.

**Tab plugin của tôi không hiển thị.**

1. Kiểm tra bảng kê khai có tại

~/.Hermes/plugins/&lt;name/dashboard/manifest.JSON
` (lưu ý thư mục con
`dashboard/

).
2.
`curl http://127.0.0.1:9119/API/dashboard/plugins/rescan
` để buộc khám phá lại.
3. Mở công cụ phát triển trình duyệt → Mạng — xác nhận
`manifest.JSON

,
`index.js
` và mọi CSS được tải mà không có 404.
4. Mở công cụ phát triển trình duyệt → Bảng điều khiển — tìm lỗi trong IIFE hoặc
`window.__Hermes_PLUGINS__ is undefined
` (cho biết SDK không khởi chạy, thường là lỗi kết xuất React trước đó).
5. Xác minh gói của bạn gọi
`window.__Hermes_PLUGINS__.register(...)
` có **cùng tên** với
`manifest.JSON:name

.

**Các thành phần đã đăng ký theo vị trí không hiển thị.**
Khe
`sidebar
` chỉ hiển thị khi chủ đề đang hoạt động có
`layoutVariant: cockpit

. Các vị trí khác luôn hiển thị. Nếu bạn đang đăng ký vào một vị trí không có lượt truy cập, hãy thêm
`console.log
` bên trong
`registerSlot
` để xác nhận gói plugin đã chạy hoàn toàn.

**Các tuyến phụ trợ plugin trả về 404.**
1. Xác nhận tệp kê khai có

"API": "plugin_API.py"
` trỏ đến tệp hiện có bên trong
`dashboard/

.
2. Khởi động lại
`Hermes dashboard
` — các tuyến API của plugin được gắn kết một lần khi khởi động, **không** khi quét lại.
3. Kiểm tra xem
`plugin_API.py
` có xuất
`router = APIRouter()
` cấp mô-đun hay không. Tên xuất khẩu khác không được chọn.
4. Đuôi

~/.Hermes/logs/errors.log
` dành cho
`Failed to load plugin &lt;name API routes
` — lỗi nhập được ghi vào đó.

**Thay đổi chủ đề sẽ loại bỏ phần ghi đè màu của tôi.**
`colorOverrides
` nằm trong phạm vi chủ đề đang hoạt động và bị xóa khi chuyển đổi chủ đề — đó là do thiết kế. Nếu bạn muốn các phần ghi đè vẫn tồn tại, hãy đặt chúng vào YAML của chủ đề chứ không phải trong trình chuyển đổi trực tiếp.

**CSS tùy chỉnh chủ đề bị cắt bớt.**
Khối
`customCSS
` được giới hạn ở mức 32 KiB cho mỗi chủ đề. Chia các biểu định kiểu lớn cho nhiều chủ đề hoặc chuyển sang một plugin chèn biểu định kiểu đầy đủ thông qua trường
``` css
` (không có giới hạn kích thước).

**Tôi muốn gửi một plugin trên PyPI.**
Các plugin bảng điều khiển được cài đặt theo bố cục thư mục chứ không phải theo điểm vào pip. Đường dẫn phân phối sạch nhất hiện nay là git repo mà người dùng sao chép vào

~/.Hermes/plugins/

. Trình cài đặt dựa trên pip cho plugin bảng điều khiển hiện chưa được kết nối.