12 KiB
name, description, version
| name | description | version |
|---|---|---|
| atomk-desktop-sidebar | Manage AtomK Desktop sidebar menu — add/remove/rename/group items, i18n syncing, QuickPromptBar readonly, Prompts CRUD page | 1.0.0 |
AtomK Desktop Sidebar Menu Management
Guide for adding, removing, renaming, or reordering sidebar menu items in AtomK Desktop (Electron app).
Session-specific references
references/atomlisting-files-media-api.md— Files menu placement, AtomListing/api/v1/media/*endpoints, storage-space UI requirements, and IPC/preload wiring pattern.
File Map (all changes touch these files)
| File | What to change |
|---|---|
src/renderer/src/screens/Layout/Layout.tsx |
View type union, NAV_ENTRIES array, NavCollapsible logic, rendering JSX, imports |
src/shared/i18n/locales/{zh-CN,en}/navigation.ts |
Add/remove/rename the key: "label" entry |
src/renderer/src/screens/{ScreenName}/{ScreenName}.tsx |
New screen component (if adding) |
src/renderer/src/assets/main.css |
New styles (if needed) |
Current Menu Structure (3 flat groups with dividers)
Group 1 — 核心: Chat / Sessions / Prompts / Skills / Tools
Group 2 — 业务: Accounts / Products / Listings / Posts / Files / Mail ← Accounts page has tabs (Stores | Social); Products/Listings/Posts/Files/Mail use AtomListing API
Group 3 — 系统: Schedules / Cloud Bridge / Settings
User-specified ordering (do not rearrange without explicit request):
- Sessions comes right after Chat in Group 1
- Accounts is the FIRST item in Group 2
- Products → Listings → Posts → Files → Mail are grouped together in Group 2
- Files belongs in the business group between Posts and Mail, not in the core group, because it connects to atomlisting.com APIs
Business group note: Products, Listings, Posts, Files, and Mail call window.hermesAPI.atomListing.* IPC → atomlisting.ts → https://www.atomlisting.com. Files must use AtomListing's media library API, not a placeholder/local file browser: getFiles() → IPC atomlisting-files-list → atomkAPI.getFiles() → GET /api/v1/media/files/; getFileFolders() → GET /api/v1/media/files/folders; getMediaOverview() → GET /api/v1/media/ for storage-space/quota. Normalize common response shapes (items, data, files, or array) and tolerate multiple storage field names. The user wants these AtomListing-backed items grouped together.
Important UX decision: Accounts does NOT use a collapsible sidebar group. Instead, it navigates to a dedicated Accounts page with internal tabs for "Stores" and "Social Accounts". The user explicitly prefers: "不是在菜单里折叠 而是在右侧主体框里分两个不同区域显示".
Nav Data Model (Layout.tsx)
The sidebar uses NAV_GROUPS: NavGroup[] — an array of flat groups:
interface NavItem { view: View; icon: LucideIcon; labelKey: string; }
interface NavGroup { items: NavItem[]; }
Historical note: A NavCollapsible type with parentLabelKey/children/childViews/items was previously used for the Accounts group, but was removed in favor of a flat "Accounts" nav item that opens a tabbed page. The NavCollapsible interface and isCollapsible() type guard still exist in Layout.tsx but are unused.
Step-by-step: Add a new menu item (flat)
-
Create screen component at
src/renderer/src/screens/{Name}/{Name}.tsx:import { IconName } from "lucide-react"; import { useI18n } from "../../components/useI18n"; function Name(): React.JSX.Element { const { t } = useI18n(); return ( <div className="screen-container"> <div className="screen-header"> <IconName size={20} /> <h2>{t("navigation.key")}</h2> </div> <div className="screen-body"> <p className="screen-placeholder">{t("common.comingSoon")}</p> </div> </div> ); } export default Name; -
Add View type — append
"name"to theViewunion type in Layout.tsx -
Add to NAV_ENTRIES — for a flat item, insert into the appropriate
NavGroup'sitemsarray:{ view: "name", icon: IconComponent, labelKey: "navigation.name" } -
Add import — import the icon from
lucide-reactAND the screen component.- Icon import pitfall: Some icons (e.g.
ChatBubble,Settings,Trash2) are re-exported with aliases from../../assets/icons. Checksrc/renderer/src/assets/icons/index.tsxfirst — if already re-exported there, import from there. Otherwise import directly fromlucide-react. Never import the same icon from both paths — duplicate identifiers cause TS errors.
- Icon import pitfall: Some icons (e.g.
-
Add rendering block in Layout.tsx:
{visitedViews.has("name") && ( <div style={paneStyle("name")}> <Name /> </div> )} -
Update 2 locale files — add
name: "Label"to eachsrc/shared/i18n/locales/{en,zh-CN}/navigation.ts -
Add CSS if needed (use existing
.screen-container/header/body/placeholderclasses for placeholder pages) -
Verify:
npx tsc --noEmit— must be zero errors -
Git:
git add -A && git commit -m "..." && git push atomk HEAD:refs/heads/main --force
Step-by-step: Add a collapsible group
Collapsible groups (like "账号/Accounts") contain a parent row that expands/collapses to reveal child items.
-
Add all child View types to the
Viewunion (same as flat items above) -
Define a NavCollapsible entry in
NAV_ENTRIES:{ parentLabelKey: "navigation.accounts", parentIcon: Users, children: [ { view: "stores", icon: Store, labelKey: "navigation.stores" }, { view: "social", icon: Share2, labelKey: "navigation.social" }, ], childViews: ["stores", "social"], // Optional: flat sibling items that share the visual group (no divider) items: [ { view: "products", icon: Package, labelKey: "navigation.products" }, ], } satisfies NavCollapsible, -
Rendering — Layout.tsx iterates
NAV_ENTRIESand usesisCollapsible()to switch rendering:- Collapsible: renders
.sidebar-nav-parent(click toggles group) +.sidebar-nav-children(animated expand) + optionalentry.items?.map(...)for flat siblings below - Flat group: renders individual
.sidebar-nav-itembuttons
- Collapsible: renders
-
State —
expandedGroups: Set<string>tracks which groups are open (keyed byparentLabelKey). Auto-expanded when navigating to a child view. -
CSS classes:
.sidebar-nav-collapsible— wrapper div.sidebar-nav-parent— clickable parent row (style matches.sidebar-nav-item).sidebar-nav-parent.has-active-child— highlight when a child is active.sidebar-nav-chevron+.rotated— chevron icon rotates 90deg when expanded.sidebar-nav-children+.open— usesgrid-template-rows: 0fr -> 1franimation.sidebar-nav-child— child items withpadding-left: 44pxindentation
-
Icon import:
ChevronRightcomes fromlucide-reactdirectly (not re-exported via assets/icons).
Step-by-step: Remove a menu item
- Remove from
NAV_ENTRIESarray (from the relevantNavGroup.itemsorNavCollapsible.children) - Remove from
Viewtype union - Remove the rendering block (
visitedViews.has(...)div) - Remove the screen component import
- Keep the component FILE (don't delete — avoids breaking other imports)
- Remove
key: "..."from bothsrc/shared/i18n/locales/{en,zh-CN}/navigation.ts - Verify + commit
Step-by-step: Rename a menu item
- Change the label string in both
src/shared/i18n/locales/{en,zh-CN}/navigation.ts - No code changes needed if only display text changes (labelKey stays the same)
Step-by-step: Add a group divider
Groups are defined in NAV_ENTRIES array. Flat NavGroup entries each render as one visible group. Collapsible NavCollapsible entries render as a single expandable group with optional flat items siblings. Dividers render between entries automatically via gi > 0 && <div className=\"sidebar-nav-divider\" />.
To merge two groups into one: move the flat items from one NavGroup into the items field of a NavCollapsible entry (or into a NavGroup that's adjacent). This removes the divider between them.
To regroup: just move { view, icon, labelKey } objects between NavGroup items arrays, or restructure as a NavCollapsible.
Key Patterns
-
EN navigation label for Mail: Use
"Mail"NOT"Email". The user explicitly corrected this. Key =navigation.mail. -
Files menu placement:
navigation.crawlercurrently displays as "Files" / "文件" and must live in Group 2 between Posts and Mail. Do not leave it in Group 1. When wiring Files to AtomListing, add all three layers:atomlisting.tsAPI method,index.tsIPC handler, andpreload/index.ts+preload/index.d.tsexposure. Seereferences/atomlisting-media-library.mdfor current/api/v1/media/*routes, storage-space fields, and normalization rules. -
QuickPromptBar reads from
DEFAULT_QUICK_PROMPTS+loadCustomPrompts()(localStorage keyatomk-custom-prompts). Addreadonly: trueto make a prompt undeletable/uneditable. -
Prompts management page at
src/renderer/src/screens/Prompts/Prompts.tsxgroups byreadonly → login → workflow → context. -
Gateway platform badge in sidebar:
window.hermesAPI.getPlatformEnabled(profile)polls every 15s, renders connected platform names after the "通讯" label. -
Icons: use
lucide-reacticons. For conflicting names (e.g.Mail), alias:import { Mail as MailIcon }. -
Alias
Settingstoo:import { Settings as SettingsIcon }.
Git Push
Remote is atomk → https://gitea9webs.sh3.ikuai7.com/admin9webs/AtomK-Desktop.git, branch main.
Force push often needed: git push atomk main --force
Note: The old clean-main branch was consolidated into main — only main branch exists now on Gitea. The gitea remote was removed due to timeout issues.
Pitfalls
- Don't forget BOTH locales (zh-CN, en) or you'll get runtime i18n key-not-found
- After adding a new icon import, make sure no name collision with existing imports (use aliases)
screen-bodydefault is centered — add.prompts-bodyoverride class if you need flex-start layout with scroll- Icon dual-import pitfall:
src/renderer/src/assets/icons/index.tsxre-exports somelucide-reacticons with aliases (e.g.ChatBubble = MessageSquare,Settings,Trash2 = Trash). If an icon is there, import from../../assets/icons; otherwise fromlucide-reactdirectly. Never both — duplicate identifiers failtsc. When in doubt,grepthe icons/index.tsx first. ChevronRightis NOT re-exported via assets/icons — always import directly fromlucide-react.- ⚠️ Navigation items silently dropped during Layout.tsx edits: When modifying Layout.tsx (e.g. removing items, reordering groups, or merging branches), it's easy to accidentally drop unrelated nav items. Always verify after Layout.tsx changes: compare the
NAV_GROUPSarray againstsrc/renderer/src/screens/directory — every screen folder with a default-exported component should have a corresponding nav entry (unless intentionally hidden). The i18nnavigation.tsfiles are a good checklist: if a key exists there but has noNAV_GROUPSentry, something was lost. This pitfall has recurred at least 4 times independently (Gateway/Messaging removal, Products/Listings/Posts/Mail, Sessions, and during group reordering). Mandatory step: after ANY Layout.tsx edit, runls src/renderer/src/screens/and compare againstNAV_GROUPS— if a screen folder has no nav entry, add it back.