Compare commits
6 Commits
v0.13.3-de
...
v0.13.3-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c7d4f99e48 | ||
|
|
d50c00afb4 | ||
|
|
0ef57df3bc | ||
|
|
0739ec857c | ||
|
|
b060ab45ff | ||
|
|
af6429162f |
31
.github/workflows/build-and-upload.yml
vendored
31
.github/workflows/build-and-upload.yml
vendored
@@ -378,7 +378,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build macOS bundle (Tauri)
|
- name: Build macOS bundle (Tauri)
|
||||||
working-directory: packages/tauri-app
|
working-directory: packages/tauri-app
|
||||||
run: npm exec -- tauri build --bundles app,zip
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (macOS)
|
- name: Package Tauri artifacts (macOS)
|
||||||
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
@@ -388,9 +388,7 @@ jobs:
|
|||||||
ARTIFACT_DIR="packages/tauri-app/release-tauri"
|
ARTIFACT_DIR="packages/tauri-app/release-tauri"
|
||||||
rm -rf "$ARTIFACT_DIR"
|
rm -rf "$ARTIFACT_DIR"
|
||||||
mkdir -p "$ARTIFACT_DIR"
|
mkdir -p "$ARTIFACT_DIR"
|
||||||
if [ -f "$BUNDLE_ROOT/macos/CodeNomad.app.zip" ]; then
|
if [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then
|
||||||
mv "$BUNDLE_ROOT/macos/CodeNomad.app.zip" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip"
|
|
||||||
elif [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then
|
|
||||||
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip"
|
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -464,7 +462,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build macOS bundle (Tauri, arm64)
|
- name: Build macOS bundle (Tauri, arm64)
|
||||||
working-directory: packages/tauri-app
|
working-directory: packages/tauri-app
|
||||||
run: npm exec -- tauri build --bundles app,zip
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (macOS arm64)
|
- name: Package Tauri artifacts (macOS arm64)
|
||||||
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
@@ -474,9 +472,7 @@ jobs:
|
|||||||
ARTIFACT_DIR="packages/tauri-app/release-tauri"
|
ARTIFACT_DIR="packages/tauri-app/release-tauri"
|
||||||
rm -rf "$ARTIFACT_DIR"
|
rm -rf "$ARTIFACT_DIR"
|
||||||
mkdir -p "$ARTIFACT_DIR"
|
mkdir -p "$ARTIFACT_DIR"
|
||||||
if [ -f "$BUNDLE_ROOT/macos/CodeNomad.app.zip" ]; then
|
if [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then
|
||||||
mv "$BUNDLE_ROOT/macos/CodeNomad.app.zip" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip"
|
|
||||||
elif [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then
|
|
||||||
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip"
|
ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
@@ -553,7 +549,7 @@ jobs:
|
|||||||
- name: Build Windows bundle (Tauri)
|
- name: Build Windows bundle (Tauri)
|
||||||
shell: bash
|
shell: bash
|
||||||
working-directory: packages/tauri-app
|
working-directory: packages/tauri-app
|
||||||
run: npm exec -- tauri build --bundles nsis,zip
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (Windows)
|
- name: Package Tauri artifacts (Windows)
|
||||||
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
@@ -563,19 +559,10 @@ jobs:
|
|||||||
$artifactDir = "packages/tauri-app/release-tauri"
|
$artifactDir = "packages/tauri-app/release-tauri"
|
||||||
if (Test-Path $artifactDir) { Remove-Item $artifactDir -Recurse -Force }
|
if (Test-Path $artifactDir) { Remove-Item $artifactDir -Recurse -Force }
|
||||||
New-Item -ItemType Directory -Path $artifactDir | Out-Null
|
New-Item -ItemType Directory -Path $artifactDir | Out-Null
|
||||||
|
$exe = Get-ChildItem -Path $bundleRoot -Recurse -File -Filter *.exe | Select-Object -First 1
|
||||||
# Use Tauri-generated zip if available
|
if ($null -ne $exe) {
|
||||||
$tauriZip = Get-ChildItem -Path "$bundleRoot/nsis" -Filter "*.zip" -File | Select-Object -First 1
|
|
||||||
if ($null -ne $tauriZip) {
|
|
||||||
$dest = Join-Path $artifactDir ("CodeNomad-Tauri-$env:VERSION-windows-x64.zip")
|
$dest = Join-Path $artifactDir ("CodeNomad-Tauri-$env:VERSION-windows-x64.zip")
|
||||||
Move-Item $tauriZip.FullName $dest -Force
|
Compress-Archive -Path $exe.Directory.FullName -DestinationPath $dest -Force
|
||||||
} else {
|
|
||||||
# Fallback: manually zip the exe
|
|
||||||
$exe = Get-ChildItem -Path $bundleRoot -Recurse -File -Filter *.exe | Select-Object -First 1
|
|
||||||
if ($null -ne $exe) {
|
|
||||||
$dest = Join-Path $artifactDir ("CodeNomad-Tauri-$env:VERSION-windows-x64.zip")
|
|
||||||
Compress-Archive -Path $exe.Directory.FullName -DestinationPath $dest -Force
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- name: Upload Actions artifacts (Tauri Windows)
|
- name: Upload Actions artifacts (Tauri Windows)
|
||||||
@@ -661,7 +648,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Build Linux bundle (Tauri)
|
- name: Build Linux bundle (Tauri)
|
||||||
working-directory: packages/tauri-app
|
working-directory: packages/tauri-app
|
||||||
run: npm exec -- tauri build --bundles appimage,deb,rpm
|
run: npm exec -- tauri build
|
||||||
|
|
||||||
- name: Package Tauri artifacts (Linux)
|
- name: Package Tauri artifacts (Linux)
|
||||||
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
|
||||||
|
|||||||
@@ -11,14 +11,7 @@
|
|||||||
"sync:version": "node ./scripts/sync-tauri-version.js",
|
"sync:version": "node ./scripts/sync-tauri-version.js",
|
||||||
"prebuild": "node ./scripts/prebuild.js",
|
"prebuild": "node ./scripts/prebuild.js",
|
||||||
"bundle:server": "npm run prebuild",
|
"bundle:server": "npm run prebuild",
|
||||||
"build": "tauri build",
|
"build": "tauri build"
|
||||||
"build:mac": "tauri build --target universal-apple-darwin --bundles app,zip",
|
|
||||||
"build:mac-arm": "tauri build --target aarch64-apple-darwin --bundles app,zip",
|
|
||||||
"build:mac-intel": "tauri build --target x86_64-apple-darwin --bundles app,zip",
|
|
||||||
"build:mac-zip": "tauri build --target universal-apple-darwin --bundles zip",
|
|
||||||
"build:win": "tauri build --bundles nsis,zip",
|
|
||||||
"build:win-zip": "tauri build --bundles zip",
|
|
||||||
"build:linux": "tauri build --bundles appimage,deb,rpm"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.9.4"
|
"@tauri-apps/cli": "^2.9.4"
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use serde_json::json;
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
use std::sync::atomic::{AtomicBool, Ordering};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use tauri::menu::{MenuBuilder, MenuItem, SubmenuBuilder};
|
use tauri::menu::{MenuBuilder, MenuItem, SubmenuBuilder};
|
||||||
use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
|
use tauri::plugin::{Builder as PluginBuilder, TauriPlugin};
|
||||||
use tauri::webview::Webview;
|
use tauri::webview::Webview;
|
||||||
@@ -32,12 +32,10 @@ use std::os::windows::ffi::OsStrExt;
|
|||||||
use windows_sys::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
|
use windows_sys::Win32::UI::Shell::SetCurrentProcessExplicitAppUserModelID;
|
||||||
|
|
||||||
static QUIT_REQUESTED: AtomicBool = AtomicBool::new(false);
|
static QUIT_REQUESTED: AtomicBool = AtomicBool::new(false);
|
||||||
static LAST_ZOOM_TIME: Mutex<Option<Instant>> = Mutex::new(None);
|
|
||||||
const DEFAULT_ZOOM_LEVEL: f64 = 1.0;
|
const DEFAULT_ZOOM_LEVEL: f64 = 1.0;
|
||||||
const ZOOM_STEP: f64 = 0.1;
|
const ZOOM_STEP: f64 = 0.1;
|
||||||
const MIN_ZOOM_LEVEL: f64 = 0.2;
|
const MIN_ZOOM_LEVEL: f64 = 0.2;
|
||||||
const MAX_ZOOM_LEVEL: f64 = 5.0;
|
const MAX_ZOOM_LEVEL: f64 = 5.0;
|
||||||
const ZOOM_DEBOUNCE_MS: u64 = 50;
|
|
||||||
|
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
|
const WINDOWS_APP_USER_MODEL_ID: &str = "ai.neuralnomads.codenomad.client";
|
||||||
@@ -259,15 +257,6 @@ fn clamp_zoom_level(value: f64) -> f64 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_main_window_zoom(app_handle: &AppHandle, next_zoom: f64) {
|
fn set_main_window_zoom(app_handle: &AppHandle, next_zoom: f64) {
|
||||||
if let Ok(mut last_zoom_time) = LAST_ZOOM_TIME.lock() {
|
|
||||||
if let Some(last_time) = *last_zoom_time {
|
|
||||||
if last_time.elapsed().as_millis() < ZOOM_DEBOUNCE_MS as u128 {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*last_zoom_time = Some(Instant::now());
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(window) = app_handle.get_webview_window("main") {
|
if let Some(window) = app_handle.get_webview_window("main") {
|
||||||
let normalized = clamp_zoom_level(next_zoom);
|
let normalized = clamp_zoom_level(next_zoom);
|
||||||
if window.set_zoom(normalized).is_ok() {
|
if window.set_zoom(normalized).is_ok() {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@
|
|||||||
"resizable": true,
|
"resizable": true,
|
||||||
"fullscreen": false,
|
"fullscreen": false,
|
||||||
"decorations": true,
|
"decorations": true,
|
||||||
"transparent": true,
|
|
||||||
"theme": "Dark",
|
"theme": "Dark",
|
||||||
"backgroundColor": "#1a1a1a",
|
"backgroundColor": "#1a1a1a",
|
||||||
"zoomHotkeysEnabled": true
|
"zoomHotkeysEnabled": true
|
||||||
@@ -53,7 +52,6 @@
|
|||||||
],
|
],
|
||||||
"targets": [
|
"targets": [
|
||||||
"app",
|
"app",
|
||||||
"zip",
|
|
||||||
"appimage",
|
"appimage",
|
||||||
"deb",
|
"deb",
|
||||||
"rpm",
|
"rpm",
|
||||||
|
|||||||
@@ -629,13 +629,12 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
const lastAssistantIdx = props.lastAssistantIndex()
|
const lastAssistantIdx = props.lastAssistantIndex()
|
||||||
const isQueued = current.role === "user" && (lastAssistantIdx === -1 || index > lastAssistantIdx)
|
const isQueued = current.role === "user" && (lastAssistantIdx === -1 || index > lastAssistantIdx)
|
||||||
|
|
||||||
// Intentionally untracked: messageInfoVersion updates should not trigger
|
const messageInfoVersion = props.store().state.messageInfoVersion[current.id] ?? 0
|
||||||
// a full message block rebuild; record revision is the invalidation key.
|
|
||||||
const info = untrack(messageInfo)
|
|
||||||
|
|
||||||
const cacheSignature = [
|
const cacheSignature = [
|
||||||
current.id,
|
current.id,
|
||||||
current.revision,
|
current.revision,
|
||||||
|
messageInfoVersion,
|
||||||
isQueued ? 1 : 0,
|
isQueued ? 1 : 0,
|
||||||
props.showThinking() ? 1 : 0,
|
props.showThinking() ? 1 : 0,
|
||||||
props.thinkingDefaultExpanded() ? 1 : 0,
|
props.thinkingDefaultExpanded() ? 1 : 0,
|
||||||
@@ -647,6 +646,9 @@ export default function MessageBlock(props: MessageBlockProps) {
|
|||||||
return cachedBlock.block
|
return cachedBlock.block
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Only capture info after cache check fails - ensures fresh data on version bump
|
||||||
|
const info = untrack(messageInfo)
|
||||||
|
|
||||||
const { orderedParts } = buildRecordDisplayData(props.instanceId, current)
|
const { orderedParts } = buildRecordDisplayData(props.instanceId, current)
|
||||||
const items: MessageBlockItem[] = []
|
const items: MessageBlockItem[] = []
|
||||||
const blockContentKeys: string[] = []
|
const blockContentKeys: string[] = []
|
||||||
@@ -1108,17 +1110,23 @@ function StepCard(props: StepCardProps) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const info = props.messageInfo
|
const info = props.messageInfo
|
||||||
if (!info || info.role !== "assistant" || !info.tokens) {
|
const part = props.part as any
|
||||||
|
|
||||||
|
// step-finish parts have tokens embedded; also check messageInfo
|
||||||
|
const partTokens = part?.tokens
|
||||||
|
const infoTokens = info && info.role === "assistant" ? info.tokens : undefined
|
||||||
|
const tokens = partTokens ?? infoTokens
|
||||||
|
if (!tokens) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const tokens = info.tokens
|
|
||||||
return {
|
return {
|
||||||
input: tokens.input ?? 0,
|
input: tokens.input ?? 0,
|
||||||
output: tokens.output ?? 0,
|
output: tokens.output ?? 0,
|
||||||
reasoning: tokens.reasoning ?? 0,
|
reasoning: tokens.reasoning ?? 0,
|
||||||
cacheRead: tokens.cache?.read ?? 0,
|
cacheRead: tokens.cache?.read ?? 0,
|
||||||
cacheWrite: tokens.cache?.write ?? 0,
|
cacheWrite: tokens.cache?.write ?? 0,
|
||||||
cost: info.cost ?? 0,
|
cost: (part?.cost ?? (info && info.role === "assistant" ? info.cost : 0)) ?? 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ const Field: Component<{
|
|||||||
<div class="settings-toggle-title">{props.label}</div>
|
<div class="settings-toggle-title">{props.label}</div>
|
||||||
<div class="settings-toggle-caption">{props.caption}</div>
|
<div class="settings-toggle-caption">{props.caption}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2 min-w-[18rem] max-w-[24rem] w-full">
|
<div class="flex items-center gap-2 w-full min-w-0 sm:min-w-[18rem] sm:max-w-[24rem]">
|
||||||
{props.icon}
|
{props.icon}
|
||||||
<input
|
<input
|
||||||
type={props.type ?? "text"}
|
type={props.type ?? "text"}
|
||||||
@@ -361,7 +361,7 @@ const SelectField: Component<{
|
|||||||
<div class="settings-toggle-title">{props.label}</div>
|
<div class="settings-toggle-title">{props.label}</div>
|
||||||
<div class="settings-toggle-caption">{props.caption}</div>
|
<div class="settings-toggle-caption">{props.caption}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-[18rem] max-w-[24rem] w-full">
|
<div class="w-full min-w-0 sm:min-w-[18rem] sm:max-w-[24rem]">
|
||||||
<select value={props.value} onInput={(event) => props.onInput(event.currentTarget.value)} class="selector-input w-full">
|
<select value={props.value} onInput={(event) => props.onInput(event.currentTarget.value)} class="selector-input w-full">
|
||||||
<For each={props.options}>{(option) => <option value={option.value}>{option.label}</option>}</For>
|
<For each={props.options}>{(option) => <option value={option.value}>{option.label}</option>}</For>
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -116,18 +116,11 @@ export function updateSessionInfo(instanceId: string, sessionId: string): void {
|
|||||||
// Prefer explicit input limits when provided by the API.
|
// Prefer explicit input limits when provided by the API.
|
||||||
// This is used by the UI "Avail" chip.
|
// This is used by the UI "Avail" chip.
|
||||||
contextAvailableTokens = modelInputLimit
|
contextAvailableTokens = modelInputLimit
|
||||||
}
|
} else if (contextWindow > 0) {
|
||||||
|
// When no explicit input limit, show full context window capacity.
|
||||||
if (!contextAvailableFromPrevious && contextAvailableTokens === null) {
|
contextAvailableTokens = contextWindow
|
||||||
if (contextWindow > 0) {
|
} else {
|
||||||
if (latestHasContextUsage && actualUsageTokens > 0) {
|
contextAvailableTokens = null
|
||||||
contextAvailableTokens = Math.max(contextWindow - (actualUsageTokens + outputBudget), 0)
|
|
||||||
} else {
|
|
||||||
contextAvailableTokens = contextWindow
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
contextAvailableTokens = null
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setSessionInfoByInstance((prev) => {
|
setSessionInfoByInstance((prev) => {
|
||||||
|
|||||||
@@ -526,14 +526,49 @@
|
|||||||
@media (max-width: 640px) {
|
@media (max-width: 640px) {
|
||||||
.settings-screen-frame {
|
.settings-screen-frame {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.modal-surface.settings-screen-shell {
|
.modal-surface.settings-screen-shell {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
max-height: none;
|
max-height: none;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-surface.settings-screen-shell .settings-screen-nav,
|
||||||
|
.modal-surface.settings-screen-shell .settings-screen-nav-list,
|
||||||
|
.modal-surface.settings-screen-shell .settings-screen-content,
|
||||||
|
.modal-surface.settings-screen-shell .settings-screen-scroll,
|
||||||
|
.modal-surface.settings-screen-shell .settings-section-stack,
|
||||||
|
.modal-surface.settings-screen-shell .settings-stack,
|
||||||
|
.modal-surface.settings-screen-shell .settings-card,
|
||||||
|
.modal-surface.settings-screen-shell .settings-card-content,
|
||||||
|
.modal-surface.settings-screen-shell .settings-toggle-row,
|
||||||
|
.modal-surface.settings-screen-shell .settings-toggle-row > * {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-surface.settings-screen-shell .selector-trigger,
|
||||||
|
.modal-surface.settings-screen-shell .selector-input,
|
||||||
|
.modal-surface.settings-screen-shell .selector-button {
|
||||||
|
min-width: 0;
|
||||||
|
max-width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-surface.settings-screen-shell .settings-toggle-caption,
|
||||||
|
.modal-surface.settings-screen-shell .settings-inline-note,
|
||||||
|
.modal-surface.settings-screen-shell .remote-address-url,
|
||||||
|
.modal-surface.settings-screen-shell code {
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-surface.settings-screen-shell .whitespace-nowrap {
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-screen-content-header,
|
.settings-screen-content-header,
|
||||||
|
|||||||
Reference in New Issue
Block a user