Files

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.tshttps://www.atomlisting.com. Files must use AtomListing's media library API, not a placeholder/local file browser: getFiles() → IPC atomlisting-files-listatomkAPI.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)

  1. 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;
    
  2. Add View type — append "name" to the View union type in Layout.tsx

  3. Add to NAV_ENTRIES — for a flat item, insert into the appropriate NavGroup's items array: { view: "name", icon: IconComponent, labelKey: "navigation.name" }

  4. Add import — import the icon from lucide-react AND the screen component.

    • Icon import pitfall: Some icons (e.g. ChatBubble, Settings, Trash2) are re-exported with aliases from ../../assets/icons. Check src/renderer/src/assets/icons/index.tsx first — if already re-exported there, import from there. Otherwise import directly from lucide-react. Never import the same icon from both paths — duplicate identifiers cause TS errors.
  5. Add rendering block in Layout.tsx:

    {visitedViews.has("name") && (
      <div style={paneStyle("name")}>
        <Name />
      </div>
    )}
    
  6. Update 2 locale files — add name: "Label" to each src/shared/i18n/locales/{en,zh-CN}/navigation.ts

  7. Add CSS if needed (use existing .screen-container/header/body/placeholder classes for placeholder pages)

  8. Verify: npx tsc --noEmit — must be zero errors

  9. 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.

  1. Add all child View types to the View union (same as flat items above)

  2. 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,
    
  3. Rendering — Layout.tsx iterates NAV_ENTRIES and uses isCollapsible() to switch rendering:

    • Collapsible: renders .sidebar-nav-parent (click toggles group) + .sidebar-nav-children (animated expand) + optional entry.items?.map(...) for flat siblings below
    • Flat group: renders individual .sidebar-nav-item buttons
  4. StateexpandedGroups: Set<string> tracks which groups are open (keyed by parentLabelKey). Auto-expanded when navigating to a child view.

  5. 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 — uses grid-template-rows: 0fr -> 1fr animation
    • .sidebar-nav-child — child items with padding-left: 44px indentation
  6. Icon import: ChevronRight comes from lucide-react directly (not re-exported via assets/icons).

Step-by-step: Remove a menu item

  1. Remove from NAV_ENTRIES array (from the relevant NavGroup.items or NavCollapsible.children)
  2. Remove from View type union
  3. Remove the rendering block (visitedViews.has(...) div)
  4. Remove the screen component import
  5. Keep the component FILE (don't delete — avoids breaking other imports)
  6. Remove key: "..." from both src/shared/i18n/locales/{en,zh-CN}/navigation.ts
  7. Verify + commit

Step-by-step: Rename a menu item

  1. Change the label string in both src/shared/i18n/locales/{en,zh-CN}/navigation.ts
  2. 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.crawler currently 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.ts API method, index.ts IPC handler, and preload/index.ts + preload/index.d.ts exposure. See references/atomlisting-media-library.md for current /api/v1/media/* routes, storage-space fields, and normalization rules.

  • QuickPromptBar reads from DEFAULT_QUICK_PROMPTS + loadCustomPrompts() (localStorage key atomk-custom-prompts). Add readonly: true to make a prompt undeletable/uneditable.

  • Prompts management page at src/renderer/src/screens/Prompts/Prompts.tsx groups by readonly → 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-react icons. For conflicting names (e.g. Mail), alias: import { Mail as MailIcon }.

  • Alias Settings too: import { Settings as SettingsIcon }.

Git Push

Remote is atomkhttps://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-body default is centered — add .prompts-body override class if you need flex-start layout with scroll
  • Icon dual-import pitfall: src/renderer/src/assets/icons/index.tsx re-exports some lucide-react icons with aliases (e.g. ChatBubble = MessageSquare, Settings, Trash2 = Trash). If an icon is there, import from ../../assets/icons; otherwise from lucide-react directly. Never both — duplicate identifiers fail tsc. When in doubt, grep the icons/index.tsx first.
  • ChevronRight is NOT re-exported via assets/icons — always import directly from lucide-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_GROUPS array against src/renderer/src/screens/ directory — every screen folder with a default-exported component should have a corresponding nav entry (unless intentionally hidden). The i18n navigation.ts files are a good checklist: if a key exists there but has no NAV_GROUPS entry, 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, run ls src/renderer/src/screens/ and compare against NAV_GROUPS — if a screen folder has no nav entry, add it back.