Compare commits
3 Commits
v0.13.3-de
...
v0.13.3-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c9c1cf21f0 | ||
|
|
c7d4f99e48 | ||
|
|
d50c00afb4 |
@@ -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
|
||||||
|
|||||||
@@ -1344,9 +1344,7 @@ function ReasoningStreamOutput(props: {
|
|||||||
if (preRef && preRef.textContent !== nextText) {
|
if (preRef && preRef.textContent !== nextText) {
|
||||||
preRef.textContent = nextText
|
preRef.textContent = nextText
|
||||||
}
|
}
|
||||||
if (followScroll.autoScroll()) {
|
followScroll.restoreAfterRender()
|
||||||
followScroll.restoreAfterRender({ forceBottom: true })
|
|
||||||
}
|
|
||||||
notifyContentRendered()
|
notifyContentRendered()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -454,7 +454,7 @@ function ToolCallDetails(props: {
|
|||||||
|
|
||||||
createEffect(() => {
|
createEffect(() => {
|
||||||
if (followScroll.autoScroll()) {
|
if (followScroll.autoScroll()) {
|
||||||
scrollHelpers.restoreAfterRender({ forceBottom: true })
|
scrollHelpers.restoreAfterRender()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export interface ToolScrollHelpers {
|
|||||||
registerContainer(element: HTMLDivElement | null, options?: { disableTracking?: boolean }): void
|
registerContainer(element: HTMLDivElement | null, options?: { disableTracking?: boolean }): void
|
||||||
handleScroll(event: Event & { currentTarget: HTMLDivElement }): void
|
handleScroll(event: Event & { currentTarget: HTMLDivElement }): void
|
||||||
renderSentinel(options?: { disableTracking?: boolean }): JSXElement | null
|
renderSentinel(options?: { disableTracking?: boolean }): JSXElement | null
|
||||||
restoreAfterRender(options?: { forceBottom?: boolean }): void
|
restoreAfterRender(): void
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolRendererContext {
|
export interface ToolRendererContext {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export interface FollowScrollHelpers {
|
|||||||
registerContainer: (element: HTMLDivElement | null | undefined, options?: { disableTracking?: boolean }) => void
|
registerContainer: (element: HTMLDivElement | null | undefined, options?: { disableTracking?: boolean }) => void
|
||||||
handleScroll: (event: Event & { currentTarget: HTMLDivElement }) => void
|
handleScroll: (event: Event & { currentTarget: HTMLDivElement }) => void
|
||||||
renderSentinel: (options?: { disableTracking?: boolean }) => JSXElement | null
|
renderSentinel: (options?: { disableTracking?: boolean }) => JSXElement | null
|
||||||
restoreAfterRender: (options?: { forceBottom?: boolean }) => void
|
restoreAfterRender: () => void
|
||||||
autoScroll: Accessor<boolean>
|
autoScroll: Accessor<boolean>
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,7 +183,7 @@ export function createFollowScroll(options: FollowScrollOptions): FollowScrollHe
|
|||||||
return <div ref={setBottomSentinel} aria-hidden="true" class={options.sentinelClassName} style={{ height: "1px" }} />
|
return <div ref={setBottomSentinel} aria-hidden="true" class={options.sentinelClassName} style={{ height: "1px" }} />
|
||||||
}
|
}
|
||||||
|
|
||||||
const restoreAfterRender = (config?: { forceBottom?: boolean }) => {
|
const restoreAfterRender = () => {
|
||||||
const container = scrollContainerRef
|
const container = scrollContainerRef
|
||||||
if (container && hasUserScrollIntent() && !isAtBottom(container)) {
|
if (container && hasUserScrollIntent() && !isAtBottom(container)) {
|
||||||
if (autoScroll()) {
|
if (autoScroll()) {
|
||||||
@@ -195,7 +195,10 @@ export function createFollowScroll(options: FollowScrollOptions): FollowScrollHe
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const shouldFollow = config?.forceBottom ?? autoScroll()
|
// Never let a render-time caller force follow mode back on after the user
|
||||||
|
// has already escaped it. Staying pinned should depend on the current
|
||||||
|
// follow state, not on a caller opting into forceBottom.
|
||||||
|
const shouldFollow = autoScroll()
|
||||||
requestAnimationFrame(() => {
|
requestAnimationFrame(() => {
|
||||||
restoreScrollPosition(shouldFollow)
|
restoreScrollPosition(shouldFollow)
|
||||||
if (shouldFollow) {
|
if (shouldFollow) {
|
||||||
|
|||||||
@@ -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