Compare commits
14
Commits
0b419702a1
..
v0.8.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9102c95468 | ||
|
|
743ad86f58 | ||
|
|
11e796fd17 | ||
|
|
ebbdbd4f55 | ||
|
|
ef0d65453e | ||
|
|
9f2ff015d7 | ||
|
|
a8c4025c01 | ||
|
|
5a1ad654b8 | ||
|
|
ec7eb2fcb4 | ||
|
|
c5ff3d1a49 | ||
|
|
8008d9bda4 | ||
|
|
ebcb3cdc2a | ||
|
|
2641edecc1 | ||
|
|
4621d734ff |
@@ -9,6 +9,8 @@ jobs:
|
||||
build-macos:
|
||||
name: Build macOS (${{ matrix.platform.name }})
|
||||
runs-on: ${{ matrix.platform.os }}
|
||||
env:
|
||||
XDG_CONFIG_HOME: ${{ runner.temp }}/.config
|
||||
strategy:
|
||||
matrix:
|
||||
platform:
|
||||
@@ -17,21 +19,31 @@ jobs:
|
||||
target: x86_64-apple-darwin
|
||||
binary_dir: macos-x86_64
|
||||
arch: x86_64
|
||||
- name: Apple Silicon
|
||||
os: macos
|
||||
target: aarch64-apple-darwin
|
||||
binary_dir: macos-aarch64
|
||||
arch: arm64
|
||||
- name: Universal (Intel + Apple Silicon)
|
||||
os: macos
|
||||
target: universal-apple-darwin
|
||||
binary_dir: macos-universal
|
||||
arch: universal
|
||||
# - name: Apple Silicon
|
||||
# os: macos
|
||||
# target: aarch64-apple-darwin
|
||||
# binary_dir: macos-aarch64
|
||||
# arch: arm64
|
||||
# - name: Universal (Intel + Apple Silicon)
|
||||
# os: macos
|
||||
# target: universal-apple-darwin
|
||||
# binary_dir: macos-universal
|
||||
# arch: universal
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure workspace and git permissions
|
||||
run: |
|
||||
mkdir -p "${{ runner.temp }}/.config/git"
|
||||
touch "${{ runner.temp }}/.config/git/ignore"
|
||||
mkdir -p .git/info
|
||||
touch .git/info/exclude
|
||||
chmod -R u+rwX .git || true
|
||||
chmod 644 .git/info/exclude || true
|
||||
git config --local core.excludesFile "${{ runner.temp }}/.config/git/ignore" || true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -59,21 +71,6 @@ jobs:
|
||||
test -x "src-tauri/argyll/${{ matrix.platform.binary_dir }}/instlist"
|
||||
test -x "src-tauri/argyll/${{ matrix.platform.binary_dir }}/targen"
|
||||
|
||||
- name: Run Rust tests
|
||||
shell: bash
|
||||
env:
|
||||
CARGO_INCREMENTAL: "0"
|
||||
CARGO_TARGET_DIR: "${{ runner.temp }}/cargo-target"
|
||||
run: |
|
||||
cd src-tauri
|
||||
if [ "${{ matrix.platform.target }}" = "universal-apple-darwin" ]; then
|
||||
cargo test
|
||||
elif [ "${{ matrix.platform.target }}" = "aarch64-apple-darwin" ] && [ "$(uname -m)" != "arm64" ]; then
|
||||
cargo test --no-run --target ${{ matrix.platform.target }}
|
||||
else
|
||||
cargo test --target ${{ matrix.platform.target }}
|
||||
fi
|
||||
|
||||
- name: Set Release Environment
|
||||
id: set_env
|
||||
shell: bash
|
||||
@@ -84,7 +81,23 @@ jobs:
|
||||
echo "SHORT_SHA=${SHORT_SHA}" >> $GITHUB_ENV
|
||||
echo "PREFIX=ICCery_${TAG}-${SHORT_SHA}-macos-${{ matrix.platform.arch }}" >> $GITHUB_ENV
|
||||
|
||||
- name: Warm up macOS GUI for DMG layout
|
||||
shell: bash
|
||||
run: |
|
||||
# Tauri's DMG bundler runs Finder AppleScript to set the background
|
||||
# picture and icon positions. On CI Macs, Finder/Aqua may be idle,
|
||||
# which causes AppleEvent timeouts (-1712). Wake them before build.
|
||||
echo "WindowServer: $(pgrep -l WindowServer || echo 'NOT RUNNING')"
|
||||
echo "console user: $(stat -f '%Su' /dev/console 2>/dev/null || echo unknown)"
|
||||
echo "whoami: $(whoami)"
|
||||
open -g -a Finder || true
|
||||
open -g -a "System Events" || true
|
||||
osascript -e 'tell application "Finder" to get name' || true
|
||||
sleep 10
|
||||
|
||||
- name: Build Tauri App
|
||||
env:
|
||||
TAURI_BUNDLER_DMG_IGNORE_CI: "true"
|
||||
run: npm run tauri build -- --target ${{ matrix.platform.target }}
|
||||
|
||||
- name: Prepare Release Assets
|
||||
@@ -93,18 +106,11 @@ jobs:
|
||||
mkdir -p release-assets
|
||||
|
||||
# Stage DMG package
|
||||
DMG_FILE=$(find src-tauri/target -type f -name "*.dmg" | head -n 1)
|
||||
DMG_FILE=$(find src-tauri/target -type f -path "*/release/bundle/dmg/*.dmg" | head -n 1)
|
||||
if [ -n "$DMG_FILE" ] && [ -f "$DMG_FILE" ]; then
|
||||
cp "$DMG_FILE" "release-assets/${PREFIX}.dmg"
|
||||
fi
|
||||
|
||||
# Stage ZIP archive of .app bundle
|
||||
APP_DIR=$(find src-tauri/target -type d -name "*.app" | head -n 1)
|
||||
if [ -n "$APP_DIR" ] && [ -d "$APP_DIR" ]; then
|
||||
APP_PARENT=$(dirname "$APP_DIR")
|
||||
APP_NAME=$(basename "$APP_DIR")
|
||||
(cd "$APP_PARENT" && zip -r "${GITHUB_WORKSPACE}/release-assets/${PREFIX}.zip" "$APP_NAME")
|
||||
fi
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
|
||||
@@ -35,6 +35,16 @@ jobs:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Ensure workspace and git permissions
|
||||
run: |
|
||||
mkdir -p "${{ runner.temp }}/.config/git"
|
||||
touch "${{ runner.temp }}/.config/git/ignore"
|
||||
mkdir -p .git/info
|
||||
touch .git/info/exclude
|
||||
chmod -R u+rwX .git || true
|
||||
chmod 644 .git/info/exclude || true
|
||||
git config --local core.excludesFile "${{ runner.temp }}/.config/git/ignore" || true
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
@@ -103,13 +113,6 @@ jobs:
|
||||
cp "$DMG_FILE" "release-assets/${PREFIX}.dmg"
|
||||
fi
|
||||
|
||||
# Stage ZIP archive of .app bundle
|
||||
APP_DIR=$(find src-tauri/target -type d -path "*/release/bundle/macos/*.app" | head -n 1)
|
||||
if [ -n "$APP_DIR" ] && [ -d "$APP_DIR" ]; then
|
||||
APP_PARENT=$(dirname "$APP_DIR")
|
||||
APP_NAME=$(basename "$APP_DIR")
|
||||
(cd "$APP_PARENT" && zip -r "${GITHUB_WORKSPACE}/release-assets/${PREFIX}.zip" "$APP_NAME")
|
||||
fi
|
||||
|
||||
- name: Upload Artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -115,7 +115,7 @@ The "Preferences" button opens the native macOS `NSPrintPanel` (not CUPS web UI
|
||||
- Record schema (`VerificationRecord` in `src-tauri/src/quality_store.rs`):
|
||||
- `id`: unique record identifier in the format `vr-<epoch_millis>-<seq>`.
|
||||
- `profile_name`: target profile filename.
|
||||
- `printer`: device name captured at print spooling (`wizardState.printerName`), or "Unknown".
|
||||
- `printer_name`: device name captured at print spooling (`wizardState.printerName`), or "Unknown".
|
||||
- `avg_de`, `max_de`, `rms_de`: CIEDE2000 metrics from `profcheck` (using `-u` JSON summary).
|
||||
- `patch_count`: number of test patches evaluated.
|
||||
- `status`: classified status using **ICCery verification bands (issue #95)**:
|
||||
@@ -124,7 +124,7 @@ The "Preferences" button opens the native macOS `NSPrintPanel` (not CUPS web UI
|
||||
- `< 3.5`: "Acceptable" (`badge-acceptable`)
|
||||
- `>= 3.5`: "Warning" (`badge-poor`)
|
||||
- `timestamp`: ISO-8601 UTC string.
|
||||
- Max capacity is 500 records; oldest records evicted on overflow.
|
||||
- Max capacity is 1,000 records; oldest records evicted on overflow.
|
||||
- Atomic file writes (`.tmp` write followed by `rename`) prevent data corruption.
|
||||
- Tauri IPC command casing:
|
||||
- Nested struct fields (`VerificationRecord`) serialize with `snake_case`.
|
||||
@@ -163,9 +163,9 @@ The "Preferences" button opens the native macOS `NSPrintPanel` (not CUPS web UI
|
||||
- Defaults to `false` ensuring 100% out-of-the-box compatibility with stock upstream ArgyllCMS binaries.
|
||||
- Subprocess error diagnostics in `chartread.js` capture `lastStderrLine` from `process:stderr`, auto-expanding the Process Output `<details>` panel with the stderr explanation if an unpatched binary rejects `-Y l`.
|
||||
|
||||
## CI & Cross-Compilation Testing
|
||||
## CI & Cross-Compilation
|
||||
|
||||
- macOS CI workflow lives in `.gitea/workflows/build-macos.yml`.
|
||||
- On Intel runner hosts, cross-compiling for Apple Silicon (`aarch64-apple-darwin`) must use `cargo test --no-run --target aarch64-apple-darwin`. This validates sidecar packaging, compilation, and link correctness without attempting to execute ARM64 binaries on an Intel CPU (`Bad CPU type in executable (os error 86)`).
|
||||
- Universal and native Intel matrix jobs run and execute full tests natively.
|
||||
- Release packaging workflows live under `.gitea/workflows/` (`build-macos.yml`, `build-linux.yml`, `build-windows.yml`).
|
||||
- Tag release builds focus exclusively on packaging via `npm run tauri build` without redundant debug-profile test compilations.
|
||||
- Local/CI cross-compilation test execution for Apple Silicon (`aarch64-apple-darwin`) on Intel hosts must use `cargo test --no-run --target aarch64-apple-darwin` to avoid executing ARM64 binaries on an x86_64 CPU (`Bad CPU type in executable (os error 86)`).
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Modern, cross-platform native desktop application for printer profiling, powered by ArgyllCMS.
|
||||
|
||||
[](https://git.i3omb.com/gronod/ICCery)
|
||||
[](https://git.i3omb.com/gronod/ICCery)
|
||||
[](https://git.i3omb.com/gronod/ICCery)
|
||||
[](https://tauri.app)
|
||||
[](LICENCE.md)
|
||||
@@ -27,7 +27,7 @@
|
||||
- **Noise Reduction**: Multi-pass sheet averaging (`average`) to eliminate spectrophotometer noise.
|
||||
4. **Stage 4 — Profile Calculation (`colprof`)**: Generate high-precision cLUT mathematical ICC/ICM profiles with configurable algorithm quality, OBA/FWA compensation (`-f`), illuminant (`-i`) and observer (`-o`) overrides, viewing-condition transforms (`-c`, `-d`), custom ambient spectrum support, descriptions, and copyright tagging.
|
||||
5. **Stage 5 — Verification, Drift Analytics & 3D Gamut (`profcheck` + `iccgamut`)**:
|
||||
- **Longitudinal Printer Drift Analytics (#95)**: Historical verification logging persisted to `verification_history.json` (up to 500 records), an interactive dual-series SVG trend chart with shaded ICCery verification reference bands, consecutive-breach alert recommendation card (detecting drift across distinct dates or $\ge 1$ hour apart), and RFC-4180 compliant CSV export.
|
||||
- **Longitudinal Printer Drift Analytics (#95)**: Historical verification logging persisted to `verification_history.json` (up to 1,000 records), an interactive dual-series SVG trend chart with shaded ICCery verification reference bands, consecutive-breach alert recommendation card (detecting drift across distinct dates or $\ge 1$ hour apart), and RFC-4180 compliant CSV export.
|
||||
- **Mathematical Accuracy Report**: Peak, Average, and RMS CIEDE2000 metrics with robust parsing of both Argyll JSON summaries (`-u`) and legacy plain-text reports.
|
||||
- **Interactive 3D Gamut Viewer**: CIELAB coordinate scaffold with crisp CSS2D labels, per-vertex true-colour profile gamut shading, layer visibility toggles and opacity sliders, camera reset (press **R**), touch controls, and bundled sRGB reference wireframe comparison.
|
||||
- 📊 **CGATS Dataset Interoperability (#94)**: Native parser for external CGATS and Argyll `.ti3` datasets with canonical normalization (0–255 scaling, field aliasing, metadata synthesis) and direct-jump workflows to Stage 4 (Profile Calculation) and Stage 5 (Verification).
|
||||
|
||||
+7
-2
@@ -14,7 +14,7 @@ ICCery is a native, cross-platform desktop application built with:
|
||||
- **Frontend**: Vanilla JS (ES Modules) + HTML5/CSS3 with a modern dark theme and responsive layout.
|
||||
- **Visualization**: Three.js WebGL engine for 3D CIELAB color gamut volumes and sRGB reference comparisons.
|
||||
- **Engine**: ArgyllCMS command-line utilities orchestrated over isolated standard stream IPC (`stdin`, `stdout`, `stderr`).
|
||||
- **Current Version**: `v0.8.2` (Production release).
|
||||
- **Current Version**: `v0.8.3` (Production release).
|
||||
|
||||
---
|
||||
|
||||
@@ -104,10 +104,15 @@ ICCery is a native, cross-platform desktop application built with:
|
||||
- [x] **macOS CI Cross-Compilation Testing**: Hardened `.gitea/workflows/build-macos.yml` by compiling Apple Silicon tests with `--no-run` on Intel runner hosts to avoid architecture execution mismatch.
|
||||
|
||||
### Milestone 12 — Future Workflow & Advanced Analytics (`v0.8.2`)
|
||||
- [x] **Printer Drift Tracking & Verification Analytics (#95)**: Track longitudinal printer drift in Stage 5 over time with historical run logging in `verification_history.json` (500 records), interactive dual-series SVG trend chart with ICCery verification reference bands, consecutive-breach alert recommendation card, and RFC-4180 CSV export.
|
||||
- [x] **Printer Drift Tracking & Verification Analytics (#95)**: Track longitudinal printer drift in Stage 5 over time with historical run logging in `verification_history.json` (1,000 records), interactive dual-series SVG trend chart with ICCery verification reference bands, consecutive-breach alert recommendation card, and RFC-4180 CSV export.
|
||||
- [x] **XY Automated Scanning Tables (#93)**: Full Stage 3 support for automated XY scanning tables (GretagMacbeth SpectroScan, X-Rite i1iO) with pure multi-line prompt classification, fiducial alignment prompts, 4-step sequence checklist, and graceful head parking on cancel.
|
||||
- [ ] ~~**Multi-Language Localization (#96)**~~: *Closed — Won't Fix* (English UI retained as standard color-management terminology).
|
||||
|
||||
### Maintenance & Reliability Release (`v0.8.3`)
|
||||
- [x] **Gamut Viewer Node Test Runner Support (#212)**: Guarded `window` and `window.__TAURI__` globals in `gamut_viewer.js` and added polyfill mock harness to `gamut_viewer.test.js` to enable automated headless test execution via `node src/js/gamut_viewer.test.js`.
|
||||
- [x] **Custom Spectrum File Picker Dialog (#210)**: Implemented native `select_spectrum_file` command wrapping Tauri file dialog with `.sp` filter for custom FWA/OBA spectrum selection in Stage 4 profile generation.
|
||||
- [x] **CGATS Dataset Import File Picker & State Synchronization (#211)**: Implemented native `select_dataset_file` command with `.ti3`, `.txt`, `.cgats`, and `.csv` filter, synchronized wizard target directory and basename upon import, and guarded against empty target states.
|
||||
|
||||
---
|
||||
|
||||
## 3. Future Roadmap
|
||||
|
||||
+18
-12
@@ -23,20 +23,26 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
## Delaunator
|
||||
Copyright (c) 2017, Mapbox
|
||||
## quickhull3d
|
||||
MIT License
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any purpose
|
||||
with or without fee is hereby granted, provided that the above copyright notice
|
||||
and this permission notice appear in all copies.
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
## Tauri Framework & Rust Dependencies
|
||||
Copyright (c) 2019-present, Tauri Programme within The Commons Conservancy.
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "iccery",
|
||||
"private": true,
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"fetch-argyll": "node scripts/fetch-argyll.mjs",
|
||||
|
||||
Generated
+1
-1
@@ -1423,7 +1423,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "iccery"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"image",
|
||||
|
||||
+12
-1
@@ -1,9 +1,20 @@
|
||||
[package]
|
||||
name = "iccery"
|
||||
version = "0.8.2"
|
||||
version = "0.8.3"
|
||||
description = "Modern Printer Profiling UI frontend for ArgyllCMS"
|
||||
authors = ["Gordon"]
|
||||
edition = "2021"
|
||||
include = [
|
||||
"src/**/*",
|
||||
"build.rs",
|
||||
"Cargo.toml",
|
||||
"Cargo.lock",
|
||||
"tauri.conf.json",
|
||||
"capabilities/**/*",
|
||||
"gen/**/*",
|
||||
"icons/**/*",
|
||||
"windows/**/*",
|
||||
]
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
|
||||
@@ -10,6 +10,12 @@ fn main() {
|
||||
|
||||
println!("cargo:rustc-env=BUILD_DATE={}", build_date);
|
||||
|
||||
println!("cargo:rerun-if-changed=build.rs");
|
||||
println!("cargo:rerun-if-changed=src");
|
||||
println!("cargo:rerun-if-changed=tauri.conf.json");
|
||||
println!("cargo:rerun-if-changed=Cargo.toml");
|
||||
println!("cargo:rerun-if-changed=argyll");
|
||||
|
||||
// Validate that ArgyllCMS sidecar binaries are staged before building
|
||||
let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default();
|
||||
let target_arch = std::env::var("CARGO_CFG_TARGET_ARCH").unwrap_or_default();
|
||||
|
||||
@@ -391,6 +391,61 @@ pub async fn select_profile_file(
|
||||
rx.await.map_err(|e| format!("Dialog channel error: {}", e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_spectrum_file(
|
||||
app: AppHandle,
|
||||
default_dir: Option<String>,
|
||||
) -> Result<Option<String>, String> {
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
|
||||
let mut builder = app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter("Argyll Spectrum File (*.sp)", &["sp"]);
|
||||
if let Some(ref dir) = default_dir {
|
||||
if !dir.trim().is_empty() {
|
||||
builder = builder.set_directory(std::path::PathBuf::from(dir));
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
builder.pick_file(move |file_path| {
|
||||
let res = file_path.map(|p| p.to_string());
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
|
||||
rx.await.map_err(|e| format!("Dialog channel error: {}", e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_dataset_file(
|
||||
app: AppHandle,
|
||||
default_dir: Option<String>,
|
||||
) -> Result<Option<String>, String> {
|
||||
use tauri_plugin_dialog::DialogExt;
|
||||
|
||||
let mut builder = app
|
||||
.dialog()
|
||||
.file()
|
||||
.add_filter(
|
||||
"Measurement Dataset (*.ti3, *.txt, *.cgats, *.csv)",
|
||||
&["ti3", "txt", "cgats", "csv"],
|
||||
);
|
||||
if let Some(ref dir) = default_dir {
|
||||
if !dir.trim().is_empty() {
|
||||
builder = builder.set_directory(std::path::PathBuf::from(dir));
|
||||
}
|
||||
}
|
||||
|
||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||
builder.pick_file(move |file_path| {
|
||||
let res = file_path.map(|p| p.to_string());
|
||||
let _ = tx.send(res);
|
||||
});
|
||||
|
||||
rx.await.map_err(|e| format!("Dialog channel error: {}", e))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn select_target_file(
|
||||
app: AppHandle,
|
||||
|
||||
@@ -51,6 +51,8 @@ pub fn run() {
|
||||
commands::inspect_dataset_preview,
|
||||
commands::select_existing_target,
|
||||
commands::select_profile_file,
|
||||
commands::select_spectrum_file,
|
||||
commands::select_dataset_file,
|
||||
commands::select_target_file,
|
||||
commands::select_directory,
|
||||
commands::send_stdin,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "ICCery",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"identifier": "com.gronod.iccery",
|
||||
"build": {
|
||||
"frontendDist": "../src"
|
||||
|
||||
+3
-3
@@ -1059,9 +1059,9 @@
|
||||
<p>Copyright (c) 2010-2023 three.js authors</p>
|
||||
<p class="license-legal-block">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br/><br/>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br/><br/>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
||||
|
||||
<h5>Delaunator</h5>
|
||||
<p>Copyright (c) 2017, Mapbox</p>
|
||||
<p class="license-legal-block">Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.<br/><br/>THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.</p>
|
||||
<h5>quickhull3d</h5>
|
||||
<p>MIT License</p>
|
||||
<p class="license-legal-block">Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:<br/><br/>The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.<br/><br/>THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
||||
|
||||
<h5>Tauri Framework & Rust Dependencies</h5>
|
||||
<p>Copyright (c) 2019-present, Tauri Programme within The Commons Conservancy.</p>
|
||||
|
||||
+40
-11
@@ -1,4 +1,6 @@
|
||||
const { invoke } = window.__TAURI__.core;
|
||||
const invoke = typeof window !== 'undefined' && window.__TAURI__?.core?.invoke
|
||||
? window.__TAURI__.core.invoke
|
||||
: async () => {};
|
||||
|
||||
export class CgatsInterop {
|
||||
constructor(appState) {
|
||||
@@ -22,26 +24,53 @@ export class CgatsInterop {
|
||||
|
||||
async handleImport() {
|
||||
try {
|
||||
const filePath = await invoke('select_target_file');
|
||||
const filePath = await invoke('select_dataset_file', {
|
||||
defaultDir: this.appState?.cwd || null,
|
||||
});
|
||||
if (!filePath) return; // User cancelled
|
||||
|
||||
// Inspect first to show modal (optional, skipping for now, directly import)
|
||||
// Since we are mocking the UI a bit for this branch, we will just import directly
|
||||
const isWindows = filePath.includes('\\');
|
||||
const sep = isWindows ? '\\' : '/';
|
||||
const parts = filePath.split(sep);
|
||||
const fileName = parts.pop();
|
||||
const fileDir = parts.join(sep);
|
||||
const fileStem = fileName.replace(/\.[^/.]+$/, '');
|
||||
|
||||
const targetCwd = this.appState?.cwd || fileDir;
|
||||
const targetBasename = this.appState?.basename || fileStem;
|
||||
|
||||
const targetBasenameInput = document.getElementById('targetBasename');
|
||||
if (targetBasenameInput && !targetBasenameInput.value.trim()) {
|
||||
targetBasenameInput.value = targetBasename;
|
||||
}
|
||||
const selectedPathDisplay = document.getElementById('selectedPathDisplay');
|
||||
if (selectedPathDisplay && (!selectedPathDisplay.textContent || selectedPathDisplay.textContent.includes('No directory') || !this.appState?.cwd)) {
|
||||
selectedPathDisplay.textContent = `Directory: ${targetCwd}`;
|
||||
}
|
||||
|
||||
if (this.appState?.setTarget) {
|
||||
await this.appState.setTarget(targetBasename, targetCwd);
|
||||
}
|
||||
|
||||
const summary = await invoke('import_measurement_dataset', {
|
||||
filePath,
|
||||
targetCwd: this.appState.cwd,
|
||||
targetBasename: this.appState.basename,
|
||||
targetCwd,
|
||||
targetBasename,
|
||||
});
|
||||
|
||||
this.appState.showNotice(`Successfully imported dataset (${summary.patch_count} patches)`, 'success');
|
||||
this.appState?.showNotice?.(`Successfully imported dataset (${summary.patch_count} patches)`, 'success');
|
||||
|
||||
// Update state to jump to stage 4
|
||||
await this.appState.updateGating();
|
||||
this.appState.currentStage = 4;
|
||||
this.appState.applyStageDOM(4);
|
||||
if (this.appState?.updateGating) {
|
||||
await this.appState.updateGating();
|
||||
}
|
||||
if (this.appState) {
|
||||
this.appState.currentStage = 4;
|
||||
this.appState.applyStageDOM?.(4);
|
||||
}
|
||||
|
||||
} catch (e) {
|
||||
this.appState.showNotice(`Failed to import dataset: ${e}`, 'error');
|
||||
this.appState?.showNotice?.(`Failed to import dataset: ${e}`, 'error');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -54,8 +54,8 @@ export function initColprof() {
|
||||
if (btnBrowseCustomSp && colprofCustomSpPath) {
|
||||
btnBrowseCustomSp.addEventListener("click", async () => {
|
||||
try {
|
||||
const selected = await window.__TAURI__.dialog.open({
|
||||
filters: [{ name: 'Spectrum', extensions: ['sp'] }]
|
||||
const selected = await invoke("select_spectrum_file", {
|
||||
defaultDir: chartreadCwd || wizardState.cwd || null,
|
||||
});
|
||||
if (selected) {
|
||||
colprofCustomSpPath.value = selected;
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { computeQuickHull } from "./vendor/quickhull.js";
|
||||
import { computeQuickHull } from "./vendor/quickhull.js";
|
||||
import { labToSrgb } from "./color_convert.js";
|
||||
|
||||
const { invoke } = window.__TAURI__.core;
|
||||
const { listen } = window.__TAURI__.event;
|
||||
const invoke = typeof window !== 'undefined' && window.__TAURI__?.core?.invoke ? window.__TAURI__.core.invoke : null;
|
||||
|
||||
let scene, camera, renderer, labelRenderer, controls;
|
||||
let currentProfileMesh = null;
|
||||
|
||||
@@ -1,20 +1,50 @@
|
||||
// Manual / browser-console tests for gamut_viewer.js and profcheck.js parsing.
|
||||
// Run in a browser/devtools console after the app has loaded:
|
||||
// Unit & console tests for gamut_viewer.js parsing.
|
||||
// Can be run in browser devtools console:
|
||||
// import('./gamut_viewer.test.js').then(m => m.runAll())
|
||||
// Or in Node:
|
||||
// node src/js/gamut_viewer.test.js
|
||||
|
||||
import { parseGamutFile } from './gamut_viewer.js';
|
||||
// Node environment polyfill for browser globals
|
||||
if (typeof window === 'undefined') {
|
||||
globalThis.window = {
|
||||
__TAURI__: {
|
||||
core: { invoke: () => Promise.resolve() },
|
||||
event: { listen: () => Promise.resolve(() => {}) }
|
||||
},
|
||||
addEventListener: () => {},
|
||||
dispatchEvent: () => {}
|
||||
};
|
||||
globalThis.document = {
|
||||
getElementById: () => null,
|
||||
querySelectorAll: () => []
|
||||
};
|
||||
}
|
||||
|
||||
const { parseGamutFile } = await import('./gamut_viewer.js');
|
||||
|
||||
let passed = 0;
|
||||
let total = 0;
|
||||
|
||||
export function runAll() {
|
||||
console.group('gamut/profcheck parser tests');
|
||||
console.group('Gamut Viewer Parser Tests');
|
||||
passed = 0;
|
||||
total = 0;
|
||||
testParseGamutBasic();
|
||||
testParseGamutDualTable();
|
||||
testParseGamutWithComments();
|
||||
console.log(`\nResults: ${passed} / ${total} tests passed.`);
|
||||
console.groupEnd();
|
||||
|
||||
if (passed !== total) {
|
||||
throw new Error(`Gamut viewer parser tests failed: ${total - passed} failure(s)`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
total++;
|
||||
const ok = JSON.stringify(actual) === JSON.stringify(expected);
|
||||
if (ok) {
|
||||
passed++;
|
||||
console.log('PASS:', message);
|
||||
} else {
|
||||
console.error('FAIL:', message, 'expected', expected, 'got', actual);
|
||||
@@ -78,3 +108,8 @@ END_DATA`;
|
||||
assertEqual(vertices.length, 4, 'commented gamut vertex count');
|
||||
assertEqual(faces.length, 2, 'commented gamut face count');
|
||||
}
|
||||
|
||||
// Auto-run if executed in Node.js
|
||||
if (typeof process !== 'undefined' && process.argv && process.argv[1]?.endsWith('gamut_viewer.test.js')) {
|
||||
runAll();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user