feat: add API Key Manager button, fix overflow, update branding

Changes:
1. Fixed MULTIX overflow issue - added max-h-full and overflow-hidden to prevent content from pushing interface out of frame

2. Added API Key Manager button in header:
   - Key icon with emerald hover effect
   - Opens modal with provider list (NomadArch Free, Ollama Cloud, OpenAI, Anthropic, OpenRouter)
   - Shows provider status and configuration

3. Updated branding:
   - Window title: 'NomadArch 1.0'
   - Loading screen: 'NomadArch 1.0 - A fork of OpenCode'
   - Updated page titles

4. Added Settings and Key icons to imports
This commit is contained in:
Gemini AI
2025-12-23 14:04:11 +04:00
Unverified
parent 37ff96f849
commit 3cd44dd0c2
5 changed files with 113 additions and 6 deletions

View File

@@ -255,6 +255,7 @@ function createWindow() {
minHeight: 600,
backgroundColor,
icon: iconPath,
title: "NomadArch 1.0",
webPreferences: {
preload: getPreloadPath(),
contextIsolation: true,

View File

@@ -6,7 +6,7 @@ import { addTask, setActiveTask } from "@/stores/task-actions";
import { messageStoreBus } from "@/stores/message-v2/bus";
import MessageBlockList from "@/components/message-block-list";
import { formatTokenTotal } from "@/lib/formatters";
import { addToTaskQueue, getSoloState, setActiveTaskId, toggleAutonomous, toggleAutoApproval } from "@/stores/solo-store";
import { addToTaskQueue, getSoloState, setActiveTaskId, toggleAutonomous, toggleAutoApproval, toggleApex } from "@/stores/solo-store";
import { getLogger } from "@/lib/logger";
import {
Command,
@@ -33,6 +33,8 @@ import {
StopCircle,
Bot,
User,
Settings,
Key,
} from "lucide-solid";
import type { InstanceMessageStore } from "@/stores/message-v2/instance-store";
import type { Task } from "@/types/session";
@@ -51,6 +53,7 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
const [chatInput, setChatInput] = createSignal("");
let scrollContainer: HTMLDivElement | undefined;
const [bottomSentinel, setBottomSentinel] = createSignal<HTMLDivElement | null>(null);
const [showApiManager, setShowApiManager] = createSignal(false);
// Scroll to bottom helper
const scrollToBottom = () => {
@@ -280,7 +283,7 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
};
return (
<main class="h-full flex flex-col bg-[#0a0a0b] text-zinc-300 font-sans selection:bg-indigo-500/30">
<main class="h-full max-h-full flex flex-col bg-[#0a0a0b] text-zinc-300 font-sans selection:bg-indigo-500/30 overflow-hidden">
{/* Header */}
<header class="h-14 px-4 flex items-center justify-between bg-zinc-900/60 backdrop-blur-xl border-b border-white/5 relative z-30 shrink-0">
<div class="flex items-center space-x-3">
@@ -332,6 +335,14 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
</div>
</Show>
{/* API Key Manager Button */}
<button
onClick={() => setShowApiManager(true)}
class="p-2 text-zinc-500 hover:text-emerald-400 transition-all hover:bg-emerald-500/10 rounded-xl active:scale-90"
title="API Key Manager"
>
<Key size={18} strokeWidth={2} />
</button>
<button class="p-2 text-zinc-500 hover:text-white transition-all hover:bg-white/5 rounded-xl active:scale-90">
<Command size={18} strokeWidth={2} />
</button>
@@ -494,7 +505,19 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
</div>
<div class="flex items-center space-x-2">
<button
<button
onClick={() => toggleApex(props.instanceId)}
title="Toggle APEX Mode (Max Priority)"
class={`flex items-center space-x-1.5 px-2 py-1 rounded-lg border transition-all ${
solo().isApex
? "bg-rose-500/20 border-rose-500/40 text-rose-400 shadow-[0_0_15px_rgba(244,63,94,0.3)]"
: "bg-white/5 border-white/5 text-zinc-500 hover:bg-white/10"
}`}
>
<Zap size={10} class={solo().isApex ? "animate-bounce" : ""} />
<span class="text-[9px] font-black uppercase tracking-tighter">Apex</span>
</button>
<button
onClick={() => toggleAutonomous(props.instanceId)}
class={`px-2 py-0.5 rounded text-[9px] font-bold uppercase border ${solo().isAutonomous
? "bg-indigo-500/20 border-indigo-500/40 text-indigo-400"
@@ -688,6 +711,80 @@ export default function MultiTaskChat(props: MultiTaskChatProps) {
</div>
</Show>
</div>
{/* API Key Manager Modal */}
<Show when={showApiManager()}>
<div class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm" onClick={() => setShowApiManager(false)}>
<div class="w-full max-w-2xl bg-zinc-900 border border-white/10 rounded-2xl shadow-2xl overflow-hidden" onClick={(e) => e.stopPropagation()}>
<header class="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<div class="flex items-center space-x-3">
<div class="w-10 h-10 rounded-xl bg-gradient-to-br from-emerald-500 to-teal-600 flex items-center justify-center">
<Key size={20} class="text-white" />
</div>
<div>
<h2 class="text-lg font-bold text-white">API Key Manager</h2>
<p class="text-xs text-zinc-500">Manage your access tokens for various AI providers</p>
</div>
</div>
<button onClick={() => setShowApiManager(false)} class="p-2 hover:bg-white/10 rounded-lg transition-colors">
<X size={20} class="text-zinc-400" />
</button>
</header>
<div class="flex h-[400px]">
{/* Sidebar */}
<div class="w-48 bg-zinc-950/50 border-r border-white/5 p-3 space-y-1">
<div class="text-[10px] font-bold text-zinc-600 uppercase tracking-widest px-2 py-1">Built-in</div>
<button class="w-full text-left px-3 py-2 rounded-lg bg-emerald-500/20 border border-emerald-500/30 text-emerald-400 text-sm font-medium">
NomadArch (Free)
</button>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-white/5 text-zinc-400 hover:text-white text-sm font-medium transition-colors">
Ollama Cloud
</button>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-white/5 text-zinc-400 hover:text-white text-sm font-medium transition-colors">
OpenAI
</button>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-white/5 text-zinc-400 hover:text-white text-sm font-medium transition-colors">
Anthropic
</button>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-white/5 text-zinc-400 hover:text-white text-sm font-medium transition-colors">
OpenRouter
</button>
<div class="text-[10px] font-bold text-zinc-600 uppercase tracking-widest px-2 py-1 mt-4">Custom</div>
<button class="w-full text-left px-3 py-2 rounded-lg hover:bg-white/5 text-zinc-400 hover:text-white text-sm font-medium transition-colors flex items-center space-x-2">
<Plus size={14} />
<span>Add Custom Provider</span>
</button>
</div>
{/* Content */}
<div class="flex-1 p-6 flex flex-col items-center justify-center">
<div class="w-16 h-16 rounded-2xl bg-emerald-500/20 flex items-center justify-center mb-4">
<Shield size={32} class="text-emerald-400" />
</div>
<h3 class="text-xl font-bold text-white mb-2">NomadArch Managed Models</h3>
<p class="text-sm text-zinc-400 text-center max-w-sm mb-6">
These models are provided free of charge as part of the NomadArch platform. No API key or configuration is required to use them.
</p>
<div class="bg-zinc-800/50 rounded-xl p-4 w-full max-w-sm space-y-3">
<div class="flex justify-between text-sm">
<span class="text-zinc-500">Providers</span>
<span class="text-white font-medium">Qwen, DeepSeek, Google</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-zinc-500">Rate Limit</span>
<span class="text-white font-medium">Generous / Unlimited</span>
</div>
<div class="flex justify-between text-sm">
<span class="text-zinc-500">Status</span>
<span class="text-emerald-400 font-bold">ACTIVE</span>
</div>
</div>
</div>
</div>
</div>
</div>
</Show>
</main>
);
}

View File

@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>CodeNomad</title>
<title>NomadArch 1.0</title>
<script>
;(function () {
try {

View File

@@ -199,9 +199,10 @@ function LoadingApp() {
return (
<div class="loading-wrapper" role="status" aria-live="polite">
<img src={iconUrl} alt="CodeNomad" class="loading-logo" width="180" height="180" />
<img src={iconUrl} alt="NomadArch" class="loading-logo" width="180" height="180" />
<div class="loading-heading">
<h1 class="loading-title">CodeNomad</h1>
<h1 class="loading-title">NomadArch 1.0</h1>
<p class="loading-subtitle" style={{ fontSize: '14px', color: '#666', marginTop: '4px' }}>A fork of OpenCode</p>
<Show when={status()}>{(statusText) => <p class="loading-status">{statusText()}</p>}</Show>
</div>
<div class="loading-card">

View File

@@ -6,6 +6,7 @@ const log = getLogger("solo")
export interface SoloState {
isAutonomous: boolean
autoApproval: boolean
isApex: boolean // New APEX Mode state
maxSteps: number
currentStep: number
activeTaskId: string | null
@@ -20,6 +21,7 @@ export function getSoloState(instanceId: string): SoloState {
return {
isAutonomous: false,
autoApproval: false,
isApex: false,
maxSteps: 50,
currentStep: 0,
activeTaskId: null,
@@ -29,6 +31,12 @@ export function getSoloState(instanceId: string): SoloState {
return state
}
export function toggleApex(instanceId: string) {
const current = getSoloState(instanceId)
setSoloState(instanceId, { isApex: !current.isApex })
log.info("APEX Mode toggled", { instanceId, isApex: !current.isApex })
}
export function setSoloState(instanceId: string, partial: Partial<SoloState>) {
setSoloStates((prev) => {
const next = new Map(prev)