refactor: merge workspace (core/ + ui/) into a single flat binary crate
integrate CLI into main.rs, no lib.rs by design (YAGNI)
This commit is contained in:
parent
9ec407d9aa
commit
1a0b8c49aa
27 changed files with 171 additions and 249 deletions
73
Cargo.toml
73
Cargo.toml
|
|
@ -1,8 +1,73 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
# Cargo.toml
|
# Cargo.toml
|
||||||
#
|
#
|
||||||
# Workspace manifest for the Noctua project.
|
# Noctua COSMIC desktop application
|
||||||
|
|
||||||
[workspace]
|
[package]
|
||||||
members = ["core", "ui"]
|
name = "noctua"
|
||||||
resolver = "2"
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "Noctua – a document viewer for the COSMIC™ desktop"
|
||||||
|
repository = "https://codeberg.org/wfx/noctua"
|
||||||
|
authors = ["Wolfgang Morawetz <wfx@mailbox.org>"]
|
||||||
|
license = "GPL-3.0-or-later"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["image", "vector", "portable"]
|
||||||
|
image = ["dep:image", "dep:kamadak-exif"]
|
||||||
|
vector = ["dep:resvg"]
|
||||||
|
portable = ["dep:pdfium-render"]
|
||||||
|
full = ["image", "vector", "portable"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
# Serialization
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
ron = "0.8"
|
||||||
|
|
||||||
|
# Feature-gated document backends
|
||||||
|
image = { version = "0.25", optional = true }
|
||||||
|
kamadak-exif = { version = "0.5", optional = true }
|
||||||
|
pdfium-render = { version = "0.8", optional = true }
|
||||||
|
resvg = { version = "0.45", optional = true }
|
||||||
|
|
||||||
|
# Logging
|
||||||
|
log = "0.4"
|
||||||
|
env_logger = "0.11"
|
||||||
|
|
||||||
|
# i18n / localization
|
||||||
|
i18n-embed = { version = "0.16", features = [
|
||||||
|
"fluent-system",
|
||||||
|
"desktop-requester",
|
||||||
|
] }
|
||||||
|
i18n-embed-fl = "0.10"
|
||||||
|
|
||||||
|
# Resource embedding
|
||||||
|
rust-embed = "8"
|
||||||
|
|
||||||
|
# Open files/URLs with the system default application
|
||||||
|
open = "5"
|
||||||
|
|
||||||
|
# Thumbnail cache (freedesktop.org spec)
|
||||||
|
dirs = "5.0"
|
||||||
|
md5 = "0.7"
|
||||||
|
png = "0.17"
|
||||||
|
|
||||||
|
# CLI argument parsing
|
||||||
|
clap = { version = "4", features = ["derive"] }
|
||||||
|
|
||||||
|
# Async runtime (required by libcosmic)
|
||||||
|
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
|
||||||
|
|
||||||
|
[dependencies.libcosmic]
|
||||||
|
git = "https://github.com/pop-os/libcosmic.git"
|
||||||
|
features = [
|
||||||
|
"a11y",
|
||||||
|
"about",
|
||||||
|
"dbus-config",
|
||||||
|
"multi-window",
|
||||||
|
"single-instance",
|
||||||
|
"tokio",
|
||||||
|
"winit",
|
||||||
|
"wayland",
|
||||||
|
"wgpu",
|
||||||
|
]
|
||||||
|
|
|
||||||
|
|
@ -1,48 +0,0 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
# core/Cargo.toml
|
|
||||||
#
|
|
||||||
# Core library crate – no UI dependencies.
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "noctua_core"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
description = "Document core library for the Noctua viewer"
|
|
||||||
repository = "https://codeberg.org/wfx/noctua"
|
|
||||||
authors = ["Wolfgang Morawetz <wfx@mailbox.org>"]
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[lib]
|
|
||||||
name = "noctua_core"
|
|
||||||
path = "src/lib.rs"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["image", "vector", "portable"]
|
|
||||||
image = ["dep:image", "dep:kamadak-exif"]
|
|
||||||
vector = ["dep:resvg"]
|
|
||||||
portable = ["dep:pdfium-render"]
|
|
||||||
full = ["image", "vector", "portable"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
# Serialization
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
|
||||||
ron = "0.8"
|
|
||||||
|
|
||||||
# Feature-gated document backends
|
|
||||||
image = { version = "0.25", optional = true }
|
|
||||||
kamadak-exif = { version = "0.5", optional = true }
|
|
||||||
pdfium-render = { version = "0.8", optional = true }
|
|
||||||
resvg = { version = "0.45", optional = true }
|
|
||||||
|
|
||||||
# Logging interface
|
|
||||||
log = "0.4"
|
|
||||||
|
|
||||||
# Thumbnail cache (freedesktop.org spec)
|
|
||||||
dirs = "5.0"
|
|
||||||
md5 = "0.7"
|
|
||||||
png = "0.17"
|
|
||||||
|
|
||||||
[dev-dependencies]
|
|
||||||
# CLI test harness (examples/cli.rs only)
|
|
||||||
clap = { version = "4", features = ["derive"] }
|
|
||||||
env_logger = "0.11"
|
|
||||||
|
|
@ -1,14 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// src/lib.rs
|
|
||||||
//
|
|
||||||
// Public API of the noctua_core library.
|
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
|
|
||||||
pub mod document;
|
|
||||||
pub mod render;
|
|
||||||
|
|
||||||
pub use document::command::DocumentCommand;
|
|
||||||
pub use document::manager::DocumentManager;
|
|
||||||
pub use document::session::SessionCommand;
|
|
||||||
pub use document::types::RawImage;
|
|
||||||
8
justfile
8
justfile
|
|
@ -60,13 +60,9 @@ check-json: (check '--message-format=json')
|
||||||
test *args:
|
test *args:
|
||||||
cargo test --workspace {{args}}
|
cargo test --workspace {{args}}
|
||||||
|
|
||||||
# Runs the COSMIC UI application
|
# Runs the application (no args = COSMIC UI, with args = CLI mode)
|
||||||
run *args:
|
run *args:
|
||||||
env RUST_BACKTRACE=full cargo run --release -p noctua_ui {{args}}
|
env RUST_BACKTRACE=full cargo run --release {{args}}
|
||||||
|
|
||||||
# Runs the CLI test harness (core only, no UI deps)
|
|
||||||
run-cli *args:
|
|
||||||
env RUST_BACKTRACE=full cargo run --example cli -p noctua_core {{args}}
|
|
||||||
|
|
||||||
# Installs files
|
# Installs files
|
||||||
install:
|
install:
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use self::model::{PortableModel, RasterModel, VectorModel};
|
||||||
|
|
||||||
/// The decoded content of a loaded document.
|
/// The decoded content of a loaded document.
|
||||||
///
|
///
|
||||||
/// This enum is internal to `noctua_core`. The UI only ever sees a `RawImage`
|
/// This enum is internal to Noctua. The UI only ever sees a `RawImage`
|
||||||
/// produced by the render subsystem – it never matches on this type.
|
/// produced by the render subsystem – it never matches on this type.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum DocumentContent {
|
pub enum DocumentContent {
|
||||||
|
|
@ -6,6 +6,3 @@
|
||||||
pub mod command;
|
pub mod command;
|
||||||
pub mod data;
|
pub mod data;
|
||||||
pub mod store;
|
pub mod store;
|
||||||
|
|
||||||
pub use command::SessionCommand;
|
|
||||||
pub use data::{CollectionKind, SessionData, SessionItem};
|
|
||||||
|
|
@ -7,6 +7,5 @@ pub mod image;
|
||||||
pub mod layout;
|
pub mod layout;
|
||||||
pub mod viewbox;
|
pub mod viewbox;
|
||||||
|
|
||||||
pub use image::RawImage;
|
|
||||||
pub use layout::PageLayout;
|
pub use layout::PageLayout;
|
||||||
pub use viewbox::ViewBox;
|
pub use viewbox::ViewBox;
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// src/error.rs
|
// src/error.rs
|
||||||
//
|
//
|
||||||
// Central error and result types for noctua_core.
|
// Central error and result types for Noctua.
|
||||||
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
|
|
@ -13,8 +13,6 @@ pub enum Error {
|
||||||
NotFound(String),
|
NotFound(String),
|
||||||
/// The file format or operation is not supported.
|
/// The file format or operation is not supported.
|
||||||
Unsupported(String),
|
Unsupported(String),
|
||||||
/// A rendering operation failed.
|
|
||||||
Render(String),
|
|
||||||
/// A session could not be loaded or saved.
|
/// A session could not be loaded or saved.
|
||||||
Session(String),
|
Session(String),
|
||||||
/// An underlying I/O error.
|
/// An underlying I/O error.
|
||||||
|
|
@ -26,7 +24,6 @@ impl fmt::Display for Error {
|
||||||
match self {
|
match self {
|
||||||
Error::NotFound(msg) => write!(f, "Not found: {msg}"),
|
Error::NotFound(msg) => write!(f, "Not found: {msg}"),
|
||||||
Error::Unsupported(msg) => write!(f, "Unsupported: {msg}"),
|
Error::Unsupported(msg) => write!(f, "Unsupported: {msg}"),
|
||||||
Error::Render(msg) => write!(f, "Render error: {msg}"),
|
|
||||||
Error::Session(msg) => write!(f, "Session error: {msg}"),
|
Error::Session(msg) => write!(f, "Session error: {msg}"),
|
||||||
Error::Io(err) => write!(f, "I/O error: {err}"),
|
Error::Io(err) => write!(f, "I/O error: {err}"),
|
||||||
}
|
}
|
||||||
|
|
@ -1,13 +1,17 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
// examples/cli.rs
|
// src/main.rs
|
||||||
//
|
//
|
||||||
// CLI test harness for the noctua_core document foundation.
|
// Entry point for the Noctua COSMIC application.
|
||||||
|
|
||||||
use clap::{ArgGroup, Parser};
|
mod document;
|
||||||
use noctua_core::document::manager::{DocumentManager, DocumentState};
|
mod error;
|
||||||
use noctua_core::document::session::command::SessionCommand;
|
mod render;
|
||||||
use noctua_core::document::session::data::{CollectionKind, SessionData};
|
|
||||||
use std::path::PathBuf;
|
use clap::Parser;
|
||||||
|
use document::manager::{DocumentManager, DocumentState};
|
||||||
|
use document::session::command::SessionCommand;
|
||||||
|
use document::session::data::{CollectionKind, SessionData};
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
// ANSI helpers
|
// ANSI helpers
|
||||||
|
|
||||||
|
|
@ -87,50 +91,45 @@ fn print_active_session(mgr: &DocumentManager) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Parser, Debug)]
|
#[derive(Parser, Debug)]
|
||||||
#[command(
|
#[command(name = "noctua", about = "A document viewer for the COSMIC desktop")]
|
||||||
name = "noctua",
|
struct Args {
|
||||||
about = "A document viewer for the COSMIC desktop",
|
|
||||||
group(
|
|
||||||
ArgGroup::new("source")
|
|
||||||
.required(true)
|
|
||||||
.args(["open_document", "open_directory", "open_session"])
|
|
||||||
)
|
|
||||||
)]
|
|
||||||
struct Opt {
|
|
||||||
/// Open a document (raster, vector, portable)
|
/// Open a document (raster, vector, portable)
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long = "open-document", value_name = "PATH")]
|
||||||
open_document: Option<PathBuf>,
|
document: Option<PathBuf>,
|
||||||
|
|
||||||
/// Open a directory
|
/// Open a directory
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long = "open-directory", value_name = "PATH")]
|
||||||
open_directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
|
|
||||||
/// Open a session file (.ron)
|
/// Open a session file (.ron)
|
||||||
#[arg(long, value_name = "PATH")]
|
#[arg(long = "open-session", value_name = "PATH")]
|
||||||
open_session: Option<PathBuf>,
|
session: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
let cli = Opt::parse();
|
let args = Args::parse();
|
||||||
let mut mgr = DocumentManager::new();
|
|
||||||
|
|
||||||
// Open source
|
if args.document.is_none() && args.directory.is_none() && args.session.is_none() {
|
||||||
|
// TODO: Launch COSMIC UI.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
heading("Opening source");
|
run_cli(args);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(path) = cli.open_session {
|
fn open_source(mgr: &mut DocumentManager, args: Args) {
|
||||||
|
if let Some(path) = args.session {
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"Open session file",
|
"Open session file",
|
||||||
SessionCommand::OpenSession { path },
|
SessionCommand::OpenSession { path },
|
||||||
);
|
);
|
||||||
} else if let Some(path) = cli.open_document {
|
} else if let Some(path) = args.document {
|
||||||
let dir = path
|
let dir = path
|
||||||
.parent()
|
.parent()
|
||||||
.map(|p| p.to_path_buf())
|
.map_or_else(|| PathBuf::from("."), Path::to_path_buf);
|
||||||
.unwrap_or_else(|| PathBuf::from("."));
|
|
||||||
|
|
||||||
let session_name = dir
|
let session_name = dir
|
||||||
.file_name()
|
.file_name()
|
||||||
|
|
@ -139,30 +138,30 @@ fn main() {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("New session '{session_name}'"),
|
&format!("New session '{session_name}'"),
|
||||||
SessionCommand::NewSession {
|
SessionCommand::NewSession {
|
||||||
name: session_name.clone(),
|
name: session_name.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("Select '{session_name}'"),
|
&format!("Select '{session_name}'"),
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: session_name.clone(),
|
name: session_name.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("Add directory {}", dir.display()),
|
&format!("Add directory {}", dir.display()),
|
||||||
SessionCommand::AddDirectoryToSession { dir },
|
SessionCommand::AddDirectoryToSession { dir },
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("Add document {}", path.display()),
|
&format!("Add document {}", path.display()),
|
||||||
SessionCommand::AddDocumentToSession { path },
|
SessionCommand::AddDocumentToSession { path },
|
||||||
);
|
);
|
||||||
} else if let Some(path) = cli.open_directory {
|
} else if let Some(path) = args.directory {
|
||||||
let session_name = path
|
let session_name = path
|
||||||
.file_name()
|
.file_name()
|
||||||
.and_then(|n| n.to_str())
|
.and_then(|n| n.to_str())
|
||||||
|
|
@ -170,69 +169,44 @@ fn main() {
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("New session '{session_name}'"),
|
&format!("New session '{session_name}'"),
|
||||||
SessionCommand::NewSession {
|
SessionCommand::NewSession {
|
||||||
name: session_name.clone(),
|
name: session_name.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("Select '{session_name}'"),
|
&format!("Select '{session_name}'"),
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: session_name.clone(),
|
name: session_name.clone(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("Add directory {}", path.display()),
|
&format!("Add directory {}", path.display()),
|
||||||
SessionCommand::AddDirectoryToSession { dir: path },
|
SessionCommand::AddDirectoryToSession { dir: path },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// State summary
|
|
||||||
|
|
||||||
heading("State after open");
|
|
||||||
|
|
||||||
match &mgr.state {
|
|
||||||
DocumentState::Empty => info("No document loaded"),
|
|
||||||
DocumentState::Loading => info("Loading…"),
|
|
||||||
DocumentState::Error(e) => err(&format!("{e}")),
|
|
||||||
DocumentState::Loaded(_) => ok("Document loaded"),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let active_name = match mgr.active_session().map(|s| s.name.clone()) {
|
fn run_navigation_test(mgr: &mut DocumentManager) {
|
||||||
Some(n) => n,
|
|
||||||
None => {
|
|
||||||
info("No active session – nothing more to test.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
print_active_session(&mgr);
|
|
||||||
|
|
||||||
// Navigation
|
|
||||||
|
|
||||||
heading("Navigation");
|
heading("Navigation");
|
||||||
|
|
||||||
exec(&mut mgr, "NextDocument", SessionCommand::NextDocument);
|
exec(mgr, "NextDocument", SessionCommand::NextDocument);
|
||||||
ok(&format!(
|
ok(&format!(
|
||||||
"After NextDocument → index {:?}",
|
"After NextDocument → index {:?}",
|
||||||
mgr.active_document_index()
|
mgr.active_document_index()
|
||||||
));
|
));
|
||||||
|
|
||||||
exec(
|
exec(mgr, "PreviousDocument", SessionCommand::PreviousDocument);
|
||||||
&mut mgr,
|
|
||||||
"PreviousDocument",
|
|
||||||
SessionCommand::PreviousDocument,
|
|
||||||
);
|
|
||||||
ok(&format!(
|
ok(&format!(
|
||||||
"After PreviousDocument → index {:?}",
|
"After PreviousDocument → index {:?}",
|
||||||
mgr.active_document_index()
|
mgr.active_document_index()
|
||||||
));
|
));
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"SelectDocument { index: 0 }",
|
"SelectDocument { index: 0 }",
|
||||||
SessionCommand::SelectDocument { index: 0 },
|
SessionCommand::SelectDocument { index: 0 },
|
||||||
);
|
);
|
||||||
|
|
@ -241,42 +215,36 @@ fn main() {
|
||||||
mgr.active_document_index()
|
mgr.active_document_index()
|
||||||
));
|
));
|
||||||
|
|
||||||
// Move operations
|
|
||||||
|
|
||||||
heading("Move operations");
|
heading("Move operations");
|
||||||
|
|
||||||
step("Before MoveDocumentDown:");
|
step("Before MoveDocumentDown:");
|
||||||
print_active_session(&mgr);
|
print_active_session(mgr);
|
||||||
|
|
||||||
exec(
|
exec(mgr, "MoveDocumentDown", SessionCommand::MoveDocumentDown);
|
||||||
&mut mgr,
|
|
||||||
"MoveDocumentDown",
|
|
||||||
SessionCommand::MoveDocumentDown,
|
|
||||||
);
|
|
||||||
ok(&format!(
|
ok(&format!(
|
||||||
"After MoveDocumentDown → index {:?}",
|
"After MoveDocumentDown → index {:?}",
|
||||||
mgr.active_document_index()
|
mgr.active_document_index()
|
||||||
));
|
));
|
||||||
|
|
||||||
exec(&mut mgr, "MoveDocumentUp", SessionCommand::MoveDocumentUp);
|
exec(mgr, "MoveDocumentUp", SessionCommand::MoveDocumentUp);
|
||||||
ok(&format!(
|
ok(&format!(
|
||||||
"After MoveDocumentUp → index {:?}",
|
"After MoveDocumentUp → index {:?}",
|
||||||
mgr.active_document_index()
|
mgr.active_document_index()
|
||||||
));
|
));
|
||||||
|
}
|
||||||
|
|
||||||
// Copy / Paste
|
fn run_copy_paste_test(mgr: &mut DocumentManager, active_name: &str) {
|
||||||
|
|
||||||
heading("Copy / Paste");
|
heading("Copy / Paste");
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"New session 'Favorites'",
|
"New session 'Favorites'",
|
||||||
SessionCommand::NewSession {
|
SessionCommand::NewSession {
|
||||||
name: "Favorites".to_string(),
|
name: "Favorites".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"New session 'Workspace'",
|
"New session 'Workspace'",
|
||||||
SessionCommand::NewSession {
|
SessionCommand::NewSession {
|
||||||
name: "Workspace".to_string(),
|
name: "Workspace".to_string(),
|
||||||
|
|
@ -288,61 +256,56 @@ fn main() {
|
||||||
print_session(session, None);
|
print_session(session, None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectSession source → SelectDocument → CopyDocument → SelectSession target → PasteDocument
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("SelectSession '{active_name}'"),
|
&format!("SelectSession '{active_name}'"),
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: active_name.clone(),
|
name: active_name.to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"SelectDocument { index: 0 }",
|
"SelectDocument { index: 0 }",
|
||||||
SessionCommand::SelectDocument { index: 0 },
|
SessionCommand::SelectDocument { index: 0 },
|
||||||
);
|
);
|
||||||
exec(&mut mgr, "CopyDocument", SessionCommand::CopyDocument);
|
exec(mgr, "CopyDocument", SessionCommand::CopyDocument);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"SelectSession 'Favorites'",
|
"SelectSession 'Favorites'",
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: "Favorites".to_string(),
|
name: "Favorites".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(&mut mgr, "PasteDocument", SessionCommand::PasteDocument);
|
exec(mgr, "PasteDocument", SessionCommand::PasteDocument);
|
||||||
|
|
||||||
step("Favorites after paste:");
|
step("Favorites after paste:");
|
||||||
print_active_session(&mgr);
|
print_active_session(mgr);
|
||||||
|
|
||||||
// Move pasted item to front via MoveDocumentTo
|
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"MoveDocumentTo { index: 0 }",
|
"MoveDocumentTo { index: 0 }",
|
||||||
SessionCommand::MoveDocumentTo { index: 0 },
|
SessionCommand::MoveDocumentTo { index: 0 },
|
||||||
);
|
);
|
||||||
step("Favorites after MoveDocumentTo(0):");
|
step("Favorites after MoveDocumentTo(0):");
|
||||||
print_active_session(&mgr);
|
print_active_session(mgr);
|
||||||
|
|
||||||
// Remove active document
|
exec(mgr, "RemoveDocument", SessionCommand::RemoveDocument);
|
||||||
|
|
||||||
exec(&mut mgr, "RemoveDocument", SessionCommand::RemoveDocument);
|
|
||||||
step("Favorites after RemoveDocument:");
|
step("Favorites after RemoveDocument:");
|
||||||
print_active_session(&mgr);
|
print_active_session(mgr);
|
||||||
|
}
|
||||||
// Session operations
|
|
||||||
|
|
||||||
|
fn run_session_test(mgr: &mut DocumentManager, active_name: &str) {
|
||||||
heading("Session operations");
|
heading("Session operations");
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
&format!("SelectSession '{active_name}'"),
|
&format!("SelectSession '{active_name}'"),
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: active_name.clone(),
|
name: active_name.to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"DuplicateSession 'Snapshot'",
|
"DuplicateSession 'Snapshot'",
|
||||||
SessionCommand::DuplicateSession {
|
SessionCommand::DuplicateSession {
|
||||||
new_name: "Snapshot".to_string(),
|
new_name: "Snapshot".to_string(),
|
||||||
|
|
@ -355,14 +318,14 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"SelectSession 'Workspace'",
|
"SelectSession 'Workspace'",
|
||||||
SessionCommand::SelectSession {
|
SessionCommand::SelectSession {
|
||||||
name: "Workspace".to_string(),
|
name: "Workspace".to_string(),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"RenameSession 'Archive'",
|
"RenameSession 'Archive'",
|
||||||
SessionCommand::RenameSession {
|
SessionCommand::RenameSession {
|
||||||
name: "Archive".to_string(),
|
name: "Archive".to_string(),
|
||||||
|
|
@ -371,9 +334,7 @@ fn main() {
|
||||||
|
|
||||||
step("All sessions after rename:");
|
step("All sessions after rename:");
|
||||||
for session in mgr.sessions() {
|
for session in mgr.sessions() {
|
||||||
let is_active = mgr
|
let is_active = mgr.active_session().is_some_and(|a| a.name == session.name);
|
||||||
.active_session()
|
|
||||||
.map_or(false, |a| a.name == session.name);
|
|
||||||
let marker = if is_active {
|
let marker = if is_active {
|
||||||
format!("{GREEN}*{RESET}")
|
format!("{GREEN}*{RESET}")
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -383,7 +344,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(
|
exec(
|
||||||
&mut mgr,
|
mgr,
|
||||||
"CloseSession (closes 'Archive')",
|
"CloseSession (closes 'Archive')",
|
||||||
SessionCommand::CloseSession,
|
SessionCommand::CloseSession,
|
||||||
);
|
);
|
||||||
|
|
@ -392,6 +353,33 @@ fn main() {
|
||||||
for session in mgr.sessions() {
|
for session in mgr.sessions() {
|
||||||
println!(" {DIM}-{RESET} {}", session.name);
|
println!(" {DIM}-{RESET} {}", session.name);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_cli(args: Args) {
|
||||||
|
let mut mgr = DocumentManager::new();
|
||||||
|
|
||||||
|
heading("Opening source");
|
||||||
|
open_source(&mut mgr, args);
|
||||||
|
|
||||||
|
heading("State after open");
|
||||||
|
|
||||||
|
match &mgr.state {
|
||||||
|
DocumentState::Empty => info("No document loaded"),
|
||||||
|
DocumentState::Loading => info("Loading…"),
|
||||||
|
DocumentState::Error(e) => err(&format!("{e}")),
|
||||||
|
DocumentState::Loaded(_) => ok("Document loaded"),
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(active_name) = mgr.active_session().map(|s| s.name.clone()) else {
|
||||||
|
info("No active session – nothing more to test.");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
print_active_session(&mgr);
|
||||||
|
|
||||||
|
run_navigation_test(&mut mgr);
|
||||||
|
run_copy_paste_test(&mut mgr, &active_name);
|
||||||
|
run_session_test(&mut mgr, &active_name);
|
||||||
|
|
||||||
heading("Done");
|
heading("Done");
|
||||||
}
|
}
|
||||||
|
|
@ -1,50 +0,0 @@
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
# ui/Cargo.toml
|
|
||||||
#
|
|
||||||
# Binary crate for the Noctua COSMIC desktop application.
|
|
||||||
|
|
||||||
[package]
|
|
||||||
name = "noctua"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
description = "Noctua – a document viewer for the COSMIC™ desktop"
|
|
||||||
repository = "https://codeberg.org/wfx/noctua"
|
|
||||||
authors = ["Wolfgang Morawetz <wfx@mailbox.org>"]
|
|
||||||
license = "GPL-3.0-or-later"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
noctua_core = { path = "../core" }
|
|
||||||
|
|
||||||
# Logging
|
|
||||||
log = "0.4"
|
|
||||||
env_logger = "0.11"
|
|
||||||
|
|
||||||
# i18n / localization
|
|
||||||
i18n-embed = { version = "0.16", features = [
|
|
||||||
"fluent-system",
|
|
||||||
"desktop-requester",
|
|
||||||
] }
|
|
||||||
i18n-embed-fl = "0.10"
|
|
||||||
|
|
||||||
# Resource embedding
|
|
||||||
rust-embed = "8"
|
|
||||||
|
|
||||||
# Open files/URLs with the system default application
|
|
||||||
open = "5"
|
|
||||||
|
|
||||||
# Async runtime (required by libcosmic)
|
|
||||||
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
|
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
|
||||||
git = "https://github.com/pop-os/libcosmic.git"
|
|
||||||
features = [
|
|
||||||
"a11y",
|
|
||||||
"about",
|
|
||||||
"dbus-config",
|
|
||||||
"multi-window",
|
|
||||||
"single-instance",
|
|
||||||
"tokio",
|
|
||||||
"winit",
|
|
||||||
"wayland",
|
|
||||||
"wgpu",
|
|
||||||
]
|
|
||||||
|
|
@ -1,8 +0,0 @@
|
||||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
||||||
// src/main.rs
|
|
||||||
//
|
|
||||||
// Entry point for the Noctua COSMIC application.
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// TODO: Initialize the COSMIC application.
|
|
||||||
}
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue