diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3fd84dd..135bab0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -110,7 +110,7 @@ jobs: releaseBody: | See the assets below to download this version. releaseDraft: false - prerelease: false + prerelease: ${{ contains(github.ref_name, '-') }} updaterJsonPreferNsis: true updaterKeepSingleGroup: true args: ${{ matrix.settings.args }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 72da518..882b38a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +## [0.3.1-beta.1] — 2026-07-09 + +### Added +- **Document tree UX overhaul** — full hierarchy view with folder/file icons, recursive tree, and slide transitions (#157) +- **Clickable breadcrumbs** — every ancestor crumb navigates to its parent/sub-doc; resolves the materialized path client-side with no extra requests (#161) + +### Fixed +- **Document tree only showed ~5 docs** — `doc_list` now defaults `limit` to 1000 (uteke-serve's `/doc/list` default of 5 was capping the client-side tree) (#159) +- **Folders couldn't be expanded/collapsed** — Svelte 5 reactivity bug: in-place `Set` mutation + same-ref reassign did not re-render `{@const}` reads; switched to immutable updates (#160) +- **Delete failed with `400 Bad Request`** — require uteke ≥ 0.7.1 (0.7.0 lacks `/doc/delete`); the version gate now rejects 0.7.0 up front with an upgrade prompt (#159) +- **False "uteke upgrade required" for remote users** — version gate used the local CLI version; now probes the remote server via `GET /health` and treats unknown remote versions leniently (#159) +- View scroll reset + document namespace/version gate for uteke v0.7.0 (#154) + +### Changed +- Removed the Participants tab from Rooms (#158) +- Release workflow now marks hyphenated tags (e.g. `-beta.1`, `-rc.1`) as GitHub prereleases automatically + +--- + ## [0.3.0] — 2026-07-07 ### Added diff --git a/package-lock.json b/package-lock.json index d209d70..8b2a154 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "corin", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "corin", - "version": "0.2.0", + "version": "0.3.0", "dependencies": { "@codemirror/autocomplete": "^6.20.1", "@codemirror/commands": "^6.10.4", diff --git a/package.json b/package.json index 9c7d7af..7c989d3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "corin", "private": true, - "version": "0.3.0", + "version": "0.3.1-beta.1", "type": "module", "scripts": { "dev": "vite", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 99107e4..6f51b32 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -400,7 +400,7 @@ dependencies = [ [[package]] name = "corin" -version = "0.2.0" +version = "0.3.1-beta.1" dependencies = [ "chrono", "dirs", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 854ef08..c1dd6f8 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "corin" -version = "0.2.0" +version = "0.3.1-beta.1" edition = "2024" description = "CorIn — Cora Intelligence. Desktop knowledge workstation." diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index 0898954..dd04aaa 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -114,6 +114,17 @@ pub struct AppState { /// HTTP client for uteke-serve (all memory CRUD + graph operations). /// None if server is not running. pub uteke_client: Option, + /// Cached installed uteke version ("X.Y.Z"), probed once at startup. + /// For local servers this is the `uteke --version` CLI output; for remote + /// servers it is the version reported by the server's `/health` (if any). + /// `None` if unknown. Used to gate features that require a minimum server + /// version (e.g. Documents → 0.7.1). + pub uteke_version: Option, + /// `true` when Corin talks to a remote uteke-serve (https/host URL). + /// Remote servers are user-managed, so an unknown version is treated + /// leniently (no upgrade banner, no hard gate) — the server's own + /// responses surface any real incompatibility. + pub uteke_remote: bool, } // --------------------------------------------------------------------------- @@ -2662,16 +2673,171 @@ pub async fn disconnect_connection( } // ── Document Engine (uteke-serve /doc/*) ───────────────────────────── +// +// Since uteke v0.7.0 (#614) documents are global — unique slugs across all +// namespaces. `namespace` is no longer accepted. Every command gates on +// `MIN_UTEKE_FOR_DOCS` ("0.7.1") so older servers never get ambiguous +// global-slug requests. Frontend reads `uteke_version_status` to render an +// upgrade banner; `uteke_self_update` runs `uteke upgrade` and re-detects. + +/// Installed vs required uteke version (for the Documents feature). +#[derive(Debug, Clone, Serialize)] +pub struct VersionStatus { + /// Detected `X.Y.Z`, or `None` if the `uteke` CLI couldn't be probed. + pub current: Option, + /// Minimum uteke version that supports global documents ("0.7.1"). + pub required: String, + /// `true` iff `current >= required` (false when current is unknown). + pub supported: bool, +} + +/// Resolve the effective uteke version for gating. +/// +/// - **Remote server**: probe the server's self-reported version via +/// `/health` (authoritative for a user-managed server). The local CLI +/// version is irrelevant — it may be older or newer than the server. +/// - **Local server**: use the cached `uteke --version` output. +async fn resolve_uteke_version( + state: &tauri::State<'_, Arc>>, +) -> (bool, Option) { + let (is_remote, client, cached) = { + let s = state.lock().await; + ( + s.uteke_remote, + s.uteke_client.clone(), + s.uteke_version.clone(), + ) + }; + if is_remote { + if let Some(client) = client + && client.is_available().await + { + let v = client.server_version().await; + return (true, v); + } + // Remote unreachable during this probe — fall back to whatever we know. + (true, cached) + } else { + (false, cached) + } +} + +/// Reject the call unless the effective uteke version meets `min`. +/// +/// For **remote** servers with an unknown version (e.g. an older server +/// that doesn't report version in `/health`) the call is allowed through — +/// remote servers are user-managed, and the server's own response surfaces +/// any real incompatibility (e.g. a 404/400 for a missing route). +async fn require_uteke_version( + state: &tauri::State<'_, Arc>>, + min: &str, +) -> Result<(), CommandError> { + let (is_remote, current) = resolve_uteke_version(state).await; + let ok = current + .as_ref() + .map(|c| crate::version_meets(c, min)) + // Remote + unknown version → don't block (user-managed server). + // Local + unknown version → block (we can't confirm capability). + .unwrap_or(is_remote); + if ok { + Ok(()) + } else { + let hint = if is_remote { + "upgrade the uteke-serve on the remote host" + } else { + "run 'uteke upgrade' or use the Update button in Documents" + }; + Err(CommandError::Uteke(format!( + "uteke {min} or newer is required for documents (current: {}). Please {hint}.", + current.as_deref().unwrap_or("unknown") + ))) + } +} + +/// Report the effective uteke version and whether it supports Documents. +/// +/// Drives the Documents upgrade banner. For remote servers with an unknown +/// version, `supported` is `true` so users are not nagged to upgrade a server +/// they manage (and which may well be newer than the local CLI). +#[tauri::command] +pub async fn uteke_version_status( + state: tauri::State<'_, Arc>>, +) -> Result { + let (is_remote, current) = resolve_uteke_version(&state).await; + let supported = current + .as_ref() + .map(|c| crate::version_meets(c, crate::MIN_UTEKE_FOR_DOCS)) + .unwrap_or(is_remote); + Ok(VersionStatus { + current, + required: crate::MIN_UTEKE_FOR_DOCS.to_string(), + supported, + }) +} + +/// Run `uteke upgrade`, then re-detect and cache the version. +/// +/// Returns the post-upgrade `VersionStatus`. Errors if the `uteke` CLI is +/// missing or the upgrade exits non-zero. +#[tauri::command] +pub async fn uteke_self_update( + state: tauri::State<'_, Arc>>, +) -> Result { + let is_remote = state.lock().await.uteke_remote; + if is_remote { + // Upgrading the local CLI won't affect a user-managed remote server. + // Just re-probe the server's version so the banner can refresh. + let (_, current) = resolve_uteke_version(&state).await; + let supported = current + .as_ref() + .map(|c| crate::version_meets(c, crate::MIN_UTEKE_FOR_DOCS)) + .unwrap_or(true); + return Ok(VersionStatus { + current, + required: crate::MIN_UTEKE_FOR_DOCS.to_string(), + supported, + }); + } + let uteke = crate::find_uteke_cli().ok_or_else(|| { + CommandError::Uteke("uteke CLI not found — install via the setup script".into()) + })?; + let out = tokio::process::Command::new(&uteke) + .arg("upgrade") + .stdin(std::process::Stdio::null()) + .output() + .await + .map_err(|e| CommandError::Uteke(format!("failed to run 'uteke upgrade': {e}")))?; + if !out.status.success() { + let stderr = String::from_utf8_lossy(&out.stderr); + return Err(CommandError::Uteke(format!( + "'uteke upgrade' failed: {stderr}" + ))); + } + let new_version = crate::detect_uteke_version(); + { + let mut s = state.lock().await; + s.uteke_version = new_version.clone(); + } + let supported = new_version + .as_ref() + .map(|c| crate::version_meets(c, crate::MIN_UTEKE_FOR_DOCS)) + .unwrap_or(false); + Ok(VersionStatus { + current: new_version, + required: crate::MIN_UTEKE_FOR_DOCS.to_string(), + supported, + }) +} /// List documents via uteke-serve POST /doc/list. #[tauri::command] pub async fn doc_list( state: tauri::State<'_, Arc>>, - namespace: Option, limit: Option, roots_only: Option, parent: Option, ) -> Result, CommandError> { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let client = { let s = state.lock().await; s.uteke_client.clone() @@ -2682,8 +2848,11 @@ pub async fn doc_list( if !client.is_available().await { return Err(CommandError::Uteke("uteke-serve not reachable".into())); } + // uteke-serve defaults /doc/list limit to 5 (shared with memory pagination). + // The document tree needs the full set client-side, so default high here. + let limit = Some(limit.unwrap_or(1000)); let docs = client - .doc_list(namespace.as_deref(), limit, roots_only, parent.as_deref()) + .doc_list(limit, roots_only, parent.as_deref()) .await .map_err(|e| CommandError::Uteke(e.to_string()))?; Ok(docs @@ -2698,8 +2867,8 @@ pub async fn doc_get( state: tauri::State<'_, Arc>>, slug: Option, id: Option, - namespace: Option, ) -> Result { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let client = { let s = state.lock().await; s.uteke_client.clone() @@ -2708,7 +2877,7 @@ pub async fn doc_get( return Err(CommandError::Uteke("uteke-serve not running".into())); }; let doc = client - .doc_get(slug.as_deref(), id.as_deref(), namespace.as_deref()) + .doc_get(slug.as_deref(), id.as_deref()) .await .map_err(|e| CommandError::Uteke(e.to_string()))?; Ok(serde_json::to_value(doc).unwrap_or_default()) @@ -2721,10 +2890,10 @@ pub async fn doc_create( slug: String, title: String, content: String, - namespace: Option, tags: Option>, parent: Option, ) -> Result { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let tags = tags.unwrap_or_default(); let client = { let s = state.lock().await; @@ -2734,14 +2903,7 @@ pub async fn doc_create( return Err(CommandError::Uteke("uteke-serve not running".into())); }; let doc = client - .doc_create( - &slug, - &title, - &content, - namespace.as_deref(), - &tags, - parent.as_deref(), - ) + .doc_create(&slug, &title, &content, &tags, parent.as_deref()) .await .map_err(|e| CommandError::Uteke(e.to_string()))?; Ok(serde_json::to_value(doc).unwrap_or_default()) @@ -2756,8 +2918,8 @@ pub async fn doc_update( title: Option, content: Option, tags: Option>, - namespace: Option, ) -> Result { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let id_or_slug = id .or(slug) .ok_or_else(|| CommandError::Uteke("provide either 'id' or 'slug'".into()))?; @@ -2771,7 +2933,6 @@ pub async fn doc_update( let doc = client .doc_update( &id_or_slug, - namespace.as_deref(), title.as_deref(), content.as_deref(), tags.as_deref(), @@ -2787,10 +2948,10 @@ pub async fn doc_update( pub async fn doc_search( state: tauri::State<'_, Arc>>, query: String, - namespace: Option, limit: Option, mode: Option, ) -> Result, CommandError> { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let client = { let s = state.lock().await; s.uteke_client.clone() @@ -2799,7 +2960,7 @@ pub async fn doc_search( return Err(CommandError::Uteke("uteke-serve not running".into())); }; let results = client - .doc_search(&query, namespace.as_deref(), limit, mode.as_deref()) + .doc_search(&query, limit, mode.as_deref()) .await .map_err(|e| CommandError::Uteke(e.to_string()))?; Ok(results @@ -2815,6 +2976,7 @@ pub async fn doc_delete( id: Option, slug: Option, ) -> Result<(), CommandError> { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let client = { let s = state.lock().await; s.uteke_client.clone() @@ -2835,8 +2997,8 @@ pub async fn doc_move( id: Option, slug: Option, new_parent: Option, - namespace: Option, ) -> Result { + require_uteke_version(&state, crate::MIN_UTEKE_FOR_DOCS).await?; let client = { let s = state.lock().await; s.uteke_client.clone() @@ -2845,12 +3007,7 @@ pub async fn doc_move( return Err(CommandError::Uteke("uteke-serve not running".into())); }; let result = client - .doc_move( - id.as_deref(), - slug.as_deref(), - new_parent.as_deref(), - namespace.as_deref(), - ) + .doc_move(id.as_deref(), slug.as_deref(), new_parent.as_deref()) .await .map_err(|e| CommandError::Uteke(e.to_string()))?; Ok(result) diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 19c28ae..c292ac1 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -195,6 +195,67 @@ fn find_uteke_serve() -> Option { None } +/// Minimum uteke version required for the global Documents feature. +/// v0.7.0 introduced global docs (#614), but document delete/move landed in +/// v0.7.1 — require it so the full doc feature set is available. +pub(crate) const MIN_UTEKE_FOR_DOCS: &str = "0.7.1"; + +/// Find the `uteke` CLI binary in PATH or ~/.local/bin. +pub(crate) fn find_uteke_cli() -> Option { + if let Some(p) = find_in_path("uteke") { + return Some(p); + } + if let Some(home) = dirs::home_dir() { + let candidate = home.join(".local/bin/uteke"); + if candidate.is_file() { + return Some(candidate); + } + } + None +} + +/// Detect the installed uteke CLI version by shelling `uteke --version`. +/// +/// Returns the parsed `X.Y.Z` string (e.g. `"0.7.0"`), or `None` if the +/// binary is missing or the output can't be parsed. The HTTP server exposes +/// no version endpoint, so the CLI is the only source of truth. +pub(crate) fn detect_uteke_version() -> Option { + let uteke = find_uteke_cli()?; + let out = std::process::Command::new(uteke) + .arg("--version") + .stdin(std::process::Stdio::null()) + .output() + .ok()?; + if !out.status.success() { + return None; + } + // Output looks like "uteke 0.7.0". Take the last whitespace-separated token + // that contains a '.' so stray suffixes don't leak in. + String::from_utf8_lossy(&out.stdout) + .split_whitespace() + .last() + .filter(|s| s.contains('.')) + .map(|s| s.to_string()) +} + +/// Compare two `X.Y.Z` version strings. Returns `true` if `current >= min`. +/// +/// If either side fails to parse, returns `false` — conservative, so an +/// unknown/unparseable version prompts an upgrade rather than passing. +pub(crate) fn version_meets(current: &str, min: &str) -> bool { + fn parse(v: &str) -> Option<(u32, u32, u32)> { + let mut parts = v.split('.'); + let major = parts.next()?.parse().ok()?; + let minor = parts.next()?.parse().ok()?; + let patch = parts.next().and_then(|p| p.parse().ok()).unwrap_or(0); + Some((major, minor, patch)) + } + match (parse(current), parse(min)) { + (Some(c), Some(m)) => c >= m, + _ => false, + } +} + /// Run the official uteke install script. /// /// Downloads `install.sh` from GitHub and pipes it to `sh`. @@ -369,6 +430,8 @@ pub fn run() { commands::reconnect_connection, commands::disconnect_connection, // Document Engine (#137) + commands::uteke_version_status, + commands::uteke_self_update, commands::doc_list, commands::doc_get, commands::doc_create, @@ -447,6 +510,17 @@ pub fn run() { let client = UtekeClient::with_auth(&server_url, auth_token); s.uteke_client = Some(client); } + + // Cache whether we're talking to a user-managed remote + // server (vs. a locally spawned uteke-serve). + s.uteke_remote = config::is_remote_url(&server_url); + + // Cache the installed uteke version for feature + // gating (e.g. Documents requires >= 0.7.1). + // For remote servers the authoritative version is + // probed live from /health in the gating commands; + // the local CLI version is only a fallback there. + s.uteke_version = detect_uteke_version(); } Err(e) => { eprintln!("Failed to open database: {e}"); @@ -524,3 +598,39 @@ fn init_schema(conn: &rusqlite::Connection) -> Result<(), rusqlite::Error> { )?; Ok(()) } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn version_meets_equal_or_higher() { + assert!(version_meets("0.7.0", "0.7.0")); + assert!(version_meets("0.7.1", "0.7.0")); + assert!(version_meets("0.8.0", "0.7.0")); + assert!(version_meets("1.0.0", "0.7.0")); + } + + #[test] + fn docs_gate_requires_0_7_1() { + // Document delete/move landed in 0.7.1; 0.7.0 must be rejected so the + // UI prompts for an upgrade instead of hitting a missing /doc/delete. + assert!(!version_meets("0.7.0", MIN_UTEKE_FOR_DOCS)); + assert!(version_meets("0.7.1", MIN_UTEKE_FOR_DOCS)); + assert!(version_meets("0.8.0", MIN_UTEKE_FOR_DOCS)); + } + + #[test] + fn version_meets_lower_rejected() { + assert!(!version_meets("0.6.7", "0.7.0")); + assert!(!version_meets("0.6.0", "0.7.0")); + } + + #[test] + fn version_meets_unparseable_is_conservative() { + // A fully unparseable current must NOT pass the gate. + // (Pre-release suffixes like "0.7.0-rc" parse to 0.7.0 and are accepted — + // acceptable, since uteke ships clean semver tags in practice.) + assert!(!version_meets("unknown", "0.7.0")); + } +} diff --git a/src-tauri/src/uteke_client.rs b/src-tauri/src/uteke_client.rs index 6d6ca0e..f5d99b0 100644 --- a/src-tauri/src/uteke_client.rs +++ b/src-tauri/src/uteke_client.rs @@ -286,6 +286,27 @@ impl UtekeClient { .unwrap_or(false) } + /// Probe the server's self-reported version via `/health`. + /// + /// Returns `Some("X.Y.Z")` when the server includes a `version` field + /// (uteke ≥ the /health-version patch), otherwise `None`. Used to gate + /// features for remote servers where the local `uteke` CLI version is + /// irrelevant — the server may be newer or older than the CLI. + pub async fn server_version(&self) -> Option { + let resp = self + .authed(self.client.get(format!("{}/health", self.base_url))) + .send() + .await + .ok()?; + if !resp.status().is_success() { + return None; + } + let body: serde_json::Value = resp.json().await.ok()?; + body.get("version") + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) + } + /// Semantic recall (vector + FTS5 hybrid search via RRF). pub async fn recall( &self, @@ -928,6 +949,11 @@ impl UtekeClient { } // ── Documents (uteke-serve /doc/*) ───────────────────────────────── + // + // Since uteke v0.7.0 (#614) documents are GLOBAL — unique slugs across all + // namespaces. These methods send no `namespace`. Callers MUST gate access + // on `crate::MIN_UTEKE_FOR_DOCS` ("0.7.1") so older servers (where docs + // were namespace-scoped) never receive ambiguous global-slug requests. /// List documents via POST /doc/list. /// @@ -935,15 +961,11 @@ impl UtekeClient { /// `parent` returns direct children of a specific document. pub async fn doc_list( &self, - namespace: Option<&str>, limit: Option, roots_only: Option, parent: Option<&str>, ) -> Result, String> { let mut body = serde_json::json!({}); - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } if let Some(limit) = limit { body["limit"] = serde_json::json!(limit); } @@ -966,12 +988,7 @@ impl UtekeClient { /// Get a single document via POST /doc/get. /// /// Accepts either `slug` or `id`. Returns full document with content. - pub async fn doc_get( - &self, - slug: Option<&str>, - id: Option<&str>, - namespace: Option<&str>, - ) -> Result { + pub async fn doc_get(&self, slug: Option<&str>, id: Option<&str>) -> Result { let mut body = serde_json::json!({}); if let Some(s) = slug { body["slug"] = serde_json::Value::String(s.to_string()); @@ -979,9 +996,6 @@ impl UtekeClient { if let Some(i) = id { body["id"] = serde_json::Value::String(i.to_string()); } - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } let resp = self .authed(self.client.post(format!("{}/doc/get", self.base_url))) @@ -998,7 +1012,6 @@ impl UtekeClient { slug: &str, title: &str, content: &str, - namespace: Option<&str>, tags: &[String], parent: Option<&str>, ) -> Result { @@ -1008,9 +1021,6 @@ impl UtekeClient { "content": content, "tags": tags, }); - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } if let Some(pid) = parent { body["parent"] = serde_json::Value::String(pid.to_string()); } @@ -1028,7 +1038,6 @@ impl UtekeClient { pub async fn doc_update( &self, id_or_slug: &str, - namespace: Option<&str>, title: Option<&str>, content: Option<&str>, tags: Option<&[String]>, @@ -1045,9 +1054,6 @@ impl UtekeClient { } else { body["slug"] = serde_json::Value::String(id_or_slug.to_string()); } - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } if let Some(t) = title { body["title"] = serde_json::Value::String(t.to_string()); } @@ -1074,16 +1080,12 @@ impl UtekeClient { pub async fn doc_search( &self, query: &str, - namespace: Option<&str>, limit: Option, mode: Option<&str>, ) -> Result, String> { let mut body = serde_json::json!({ "query": query, }); - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } if let Some(limit) = limit { body["limit"] = serde_json::json!(limit); } @@ -1127,7 +1129,6 @@ impl UtekeClient { id: Option<&str>, slug: Option<&str>, new_parent: Option<&str>, - namespace: Option<&str>, ) -> Result { let mut body = serde_json::json!({}); if let Some(i) = id { @@ -1139,9 +1140,6 @@ impl UtekeClient { if let Some(np) = new_parent { body["new_parent"] = serde_json::Value::String(np.to_string()); } - if let Some(ns) = namespace { - body["namespace"] = serde_json::Value::String(ns.to_string()); - } let resp = self .authed(self.client.post(format!("{}/doc/move", self.base_url))) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 1c74675..4fdb943 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://raw.githubusercontent.com/tauri-apps/tauri/dev/crates/tauri-config-schema/schema.json", "productName": "CorIn", - "version": "0.3.0", + "version": "0.3.1-beta.1", "identifier": "dev.codecora.corin", "build": { "frontendDist": "../dist", diff --git a/src/App.svelte b/src/App.svelte index e30251d..0fe1d53 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -164,7 +164,7 @@ {:else if activeView === 'rooms'} {:else if activeView === 'documents'} - + {/if} {/key} diff --git a/src/lib/components/Dashboard.svelte b/src/lib/components/Dashboard.svelte index 49a71ff..ac6428c 100644 --- a/src/lib/components/Dashboard.svelte +++ b/src/lib/components/Dashboard.svelte @@ -82,7 +82,8 @@ - {#if loading} +
+ {#if loading}
Loading...
{:else} @@ -145,15 +146,27 @@ {/if}
{/if} +