Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/tikzarium-app/QA_B07_NATIVE_DRAWING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# B07 native TkZ GUI screenshot smoke · all NOT_RUN
For each OS record exact PR HEAD, source hash, OS version, CPU/GPU, DPI, MiKTeX/TeX Live engine path/version, Poppler version, PDF SHA and screenshots with SHA-256. Windows 10 x64 **physical** and Mac **physical** M1/M4 are required. Windows Server 2022 or hosted macOS CI does NOT substitute.

| Scenario | Check | Win10 | Mac M1 | Mac M4 |
| --- | --- | --- | --- | --- |
| Split 1366x768 | Source, exact PDF+guide and properties/help visible together | NOT_RUN | NOT_RUN | NOT_RUN |
| Create six primitives | Palette click/drag -> source selected -> TeX command | NOT_RUN | NOT_RUN | NOT_RUN |
| Parameters | x/y, circle radius, cubic bend, node text -> narrow undoable patch | NOT_RUN | NOT_RUN | NOT_RUN |
| Source-first | Opaque macros/comments/multiple pictures/tampered marker never overwritten | NOT_RUN | NOT_RUN | NOT_RUN |
| F1/hover help | Russian syntax, type/units, default/UNKNOWN, example, source section, prerequisites | NOT_RUN | NOT_RUN | NOT_RUN |
| Load diff | Installed-but-not-loaded offers explicit usepackage diff + undo, never automatic install | NOT_RUN | NOT_RUN | NOT_RUN |
| Build | Save -> external XeLaTeX -> exact PDF; verify different guide/PDF, log & checksum | NOT_RUN | NOT_RUN | NOT_RUN |
| Reopen/undo | Reopen generated .tex, select command, change, undo; preserve other bytes | NOT_RUN | NOT_RUN | NOT_RUN |
| Native UX | Open/Save As, Cyrillic IME, Light/Dark/Midnight/Paper, compact/comfortable | NOT_RUN | NOT_RUN | NOT_RUN |
| Scaling | Windows 100/125/150%, Retina, no collapsed controls | NOT_RUN | NOT_RUN | NOT_RUN |

Screenshots B07_<OS>_<HEAD>_<scenario>.png. Attach full command log, original/after diff, package statuses. No screenshot/host PASS is claimed by writing this protocol. The B07 guide is a separate rough pad, never an overlay precisely tied to arbitrary TikZ transforms.
30 changes: 30 additions & 0 deletions crates/tikzarium-app/QA_B08_LIBRARY_HELP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# GUI QA B08 — native TikZ via shared C catalogue

Status as of 2026-10-31: test design / NOT_RUN on physical Win10, M1, M4. This is not a screenshot result.

## Expected walkthrough

Launch with the user's external XeLaTeX configuration. Open a scratch project that explicitly loads tikz. In Split mode ensure three adjacent areas are visible: source editor, exact XeLaTeX PDF (read-only) with a separately marked *approximate* guide pad, and compact inspector/help. Draw a line, rectangle, circle, cubic Bézier, and text/math node by click/drag. After each action inspect the inserted TikZ command in source and selection highlight. Modify known numeric parameters, click Apply, Build with external XeLaTeX, check PDF and Undo, Save/Open, compare all unrelated source bytes.

Arrow is a distinct negative/positive test. With tikz installed and explicitly loaded but no arrows.meta, the Arrow tool must explain its unavailable state and provide the exact proposed \usetikzlibrary{arrows.meta} preamble insertion. Approve it, insert arrow, change its tip Stealth/Latex, Undo source edit, Undo library addition. Other tools must remain available without arrows.meta. Do not confuse availability of .sty files with project imports.

In F1 help and hover for each tool inspect the SAME C ToolSpec used by the toolbar; inspect all exposed C ParameterSpec values: TikZ syntax, kind/type, unit, permitted enum, editor preset vs verified PGF default (or UNKNOWN), Russian explanation, effect, example, warnings and manual section. Switch node between escaped text (e.g. Привет_мир) and user-entered math (e.g. \alpha+\beta); verify source string and real PDF.

Manual direct Source changes and unknown macros must be preserved byte-for-byte, invalidate visual undo safely and never become a new visually editable object. Reload recognized B08 commands, edit known ones only. For multiple tikzpictures or opaque transforms the guide must not pretend to map to arbitrary PDF geometry.

Check Light/Dark/Midnight/Paper and compact/comfortable persistence separately from unchanged TeX/PDF. At 1366x768 and Retina capture full-window screenshots. Check toolbar, hover, F1, file dialog, Unicode Windows/macOS paths, keyboard shortcuts and IME Cyrillic. Mac M1 and M4 require separate physical QA; hosted macOS ARM jobs are build evidence only.

## Evidence slots

- Exact B08 SHA and cargo fmt/check/test/clippy logs: PENDING.
- C07 catalog dependency exact SHA and generator fixture parity: PENDING.
- XeLaTeX six-primitives manual Linux fixture and PDF visual: PASS, SHA in insights.
- Self-contained repository fixture and CI: NOT_RUN until workflow.
- Windows hosted and macOS hosted app tests: PENDING.
- Physical Windows 10 + separately installed MiKTeX / Mac M1 / Mac M4: NOT_RUN.
- Screenshots, actual GUI drag + save/reopen/undo and accessibility at both densities: NOT_RUN.

Do not label the B08 PR merge-ready until exact-head tests and integration review pass.


A local exact-text copy of the B08 checked-in six-primitives ASCII fixture was separately compiled by Linux XeLaTeX with no-shell-escape: PASS exit 0, one-page PDF and visual raster inspection. Source SHA-256 278c9219a27d4e49bf9178de3d238fe3fdcaf5f6b6c8e78dae53ade7f1fe5eba; PDF SHA-256 ece7edf94a03bfd445427813a59244276d9ab77b016b0c610c004e1154db9883. This is source-level verification, not GUI acceptance or compiled C generator parity. B08 Rust CI on ceab19a9 is blocked by C07 malformed source; no app CI PASS or physical GUI PASS claimed.
15 changes: 15 additions & 0 deletions crates/tikzarium-app/fixtures/b07_native_six.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
\documentclass{standalone}
\usepackage{fontspec}
\usepackage{tikz}
\begin{document}
\begin{tikzpicture}
% Existing opaque user content must stay byte-for-byte unchanged.
\path (0,0);
\draw (0.00,0.00) -- (2.00,1.00); % tikzarium:b07:1:line
\draw[->] (0.00,1.00) -- (2.00,2.00); % tikzarium:b07:2:arrow
\draw (2.00,0.00) rectangle (4.00,1.00); % tikzarium:b07:3:rectangle
\draw (4.00,2.00) circle[radius=0.75cm]; % tikzarium:b07:4:circle
\node at (1.00,2.50) {Text}; % tikzarium:b07:5:node
\draw (0.00,3.00) .. controls (0.67,3.80) and (1.33,3.80) .. (2.00,3.00); % tikzarium:b07:6:cubic
\end{tikzpicture}
\end{document}
17 changes: 17 additions & 0 deletions crates/tikzarium-app/fixtures/b08_catalog_six.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{arrows.meta}
\pagestyle{empty}
\newcommand{\usersymbol}{Original user macro}
\begin{document}
\begin{tikzpicture}
\draw (0.00cm,0.00cm) -- (2.00cm,1.00cm); % tikzarium:b08:1:line
\draw[-{Stealth}] (0.00cm,1.00cm) -- (2.00cm,1.00cm); % tikzarium:b08:2:arrow
\draw (0.00cm,0.00cm) rectangle (2.00cm,1.00cm); % tikzarium:b08:3:rectangle
\draw (1.00cm,1.00cm) circle[radius=0.80cm]; % tikzarium:b08:4:circle
\node at (3.00cm,1.00cm) {Text}; % tikzarium:b08:5:node
\draw (0.00cm,0.00cm) .. controls (1.00cm,1.60cm) and (2.00cm,1.60cm) .. (3.00cm,1.00cm); % tikzarium:b08:6:cubic
\node at (3.00cm,0.00cm) {$\alpha+\beta$};
\node at (3.00cm,-1.00cm) {\usersymbol};
\end{tikzpicture}
\end{document}
122 changes: 95 additions & 27 deletions crates/tikzarium-app/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ mod font_proof;
mod pdf_navigation;
mod preview_image;
mod source_fonts;
mod tikz_authoring;
mod tikz_canvas;
mod ui_state;
mod ui_theme;
use ui_state::{
Expand Down Expand Up @@ -64,6 +66,8 @@ struct TikzariumApp {
show_log: bool,
pending_open: Option<PathBuf>,
prefs: UiPreferences,
authoring: tikz_authoring::Session,
highlight_generated_line: bool,
font_proof_rx: Option<Receiver<Result<FontProofResult, String>>>,
font_proof_status: String,
font_proof_report: String,
Expand Down Expand Up @@ -112,6 +116,12 @@ impl TikzariumApp {
show_log: true,
pending_open: None,
prefs,
authoring: {
let mut state = tikz_authoring::Session::default();
state.reset(QUICKSTART);
state
},
highlight_generated_line: false,
font_proof_rx: None,
font_proof_status: "Not tested".into(),
font_proof_report: String::new(),
Expand Down Expand Up @@ -140,6 +150,8 @@ impl TikzariumApp {
Ok(document) => {
self.file_field = path.display().to_string();
self.document = document;
self.authoring.reset(&self.document.source);
self.highlight_generated_line = false;
self.built_source = None;
self.built_path = None;
self.building_source = None;
Expand Down Expand Up @@ -511,13 +523,30 @@ impl TikzariumApp {
.desired_width(f32::INFINITY)
.desired_rows(36)
.show(ui);
if output.response.changed() {
self.authoring.note_manual_edit(&self.document.source);
}
if let Some(line) = self.jump_to_line.take() {
if let Some(position) = line_start_char_index(&self.document.source, line) {
use egui::text::{CCursor, CCursorRange};
output
.state
.cursor
.set_char_range(Some(CCursorRange::one(CCursor::new(position))));
output.state.cursor.set_char_range(Some(
if self.highlight_generated_line {
let count = self
.document
.source
.lines()
.nth(line.saturating_sub(1))
.map(|text| text.trim_end_matches('\r').chars().count())
.unwrap_or(0);
CCursorRange::two(
CCursor::new(position),
CCursor::new(position + count),
)
} else {
CCursorRange::one(CCursor::new(position))
},
));
self.highlight_generated_line = false;
output.state.store(ui.ctx(), output.response.id);
output.response.request_focus();
let row_height = ui.text_style_height(&egui::TextStyle::Monospace);
Expand All @@ -537,6 +566,48 @@ impl TikzariumApp {
});
}

fn primitive_capabilities(&self) -> tikz_canvas::Capabilities {
let core = self
.package_statuses
.iter()
.find(|status| status.manifest.id == "org.tikzarium.core");
tikz_canvas::Capabilities {
installed: core.is_some_and(|status| status.installed),
loaded: tikz_authoring::tikz_loaded(&self.document.source),
engine_compatible: self.tex.xelatex.is_some()
&& core.is_some_and(|status| {
status.compilation != tikzarium_extensions::CompilationState::IncompatibleEngine
}),
implemented: true, // B handles scoped interaction; C owns syntax and generation.
arrow_library_loaded: tikz_authoring::library_loaded(
&self.document.source,
"arrows.meta",
),
}
}

fn show_authoring_inspector(&mut self, ui: &mut egui::Ui) {
let caps = self.primitive_capabilities();
if let Some(line) =
tikz_canvas::show_inspector(ui, &mut self.authoring, &mut self.document.source, caps)
{
self.jump_to_line = Some(line);
self.highlight_generated_line = true;
}
if self.show_packages {
ui.separator();
ui.collapsing("Library inventory · from C registry", |ui| {
for item in &self.package_statuses {
ui.strong(&item.manifest.display_name);
ui.small(format!(
"installed={:?}, compilation={:?}, inspectable={}, editable={}",
item.installation, item.compilation, item.inspectable, item.visual_editable,
));
}
});
}
}

fn show_pdf_canvas(&mut self, ui: &mut egui::Ui) {
let canvas_width = ui.available_width();
ui.horizontal_wrapped(|ui| {
Expand Down Expand Up @@ -597,7 +668,14 @@ impl TikzariumApp {
}
ui.label(&self.preview_status);
ui.small(&self.page_navigation_status);
ui.small("Pages are rasterized from the original PDF by Poppler, without changing TeX. Canvas edits are not implemented.");
ui.small("Only the guide pad below makes B08 C-catalogue source patches. This exact PDF is read-only and is NOT coordinate-aligned with the guide.");
let caps = self.primitive_capabilities();
if let Some(line) =
tikz_canvas::show_guide(ui, &mut self.authoring, &mut self.document.source, caps)
{
self.jump_to_line = Some(line);
self.highlight_generated_line = true;
}
ui.separator();
egui::ScrollArea::both()
.auto_shrink([false, false])
Expand Down Expand Up @@ -793,6 +871,9 @@ impl eframe::App for TikzariumApp {
self.poll_pages(ctx);
self.poll_font_proof(ctx);
self.show_font_lab(ctx);
if ctx.input(|input| input.key_pressed(egui::Key::F1)) {
self.authoring.help = !self.authoring.help;
}
let shortcut = ctx.input(|input| {
if input.key_pressed(egui::Key::S) {
editor_command(egui::Key::S, input.modifiers)
Expand Down Expand Up @@ -897,7 +978,7 @@ impl eframe::App for TikzariumApp {
if ui.button("Figure fonts").clicked() {
self.prefs.show_font_lab = !self.prefs.show_font_lab;
}
ui.checkbox(&mut self.show_packages, "Packages");
ui.checkbox(&mut self.show_packages, "Library inventory");
ui.checkbox(&mut self.show_log, "Diagnostics / log");
if self.document.dirty() {
ui.colored_label(egui::Color32::YELLOW, "Unsaved edits");
Expand All @@ -908,29 +989,15 @@ impl eframe::App for TikzariumApp {
ui.label(&self.status);
});
});
if self.show_packages {
egui::SidePanel::right("packages")
if self.workspace.show_canvas() {
egui::SidePanel::right("tikz-source-inspector")
.resizable(true)
.default_width(245.0)
.default_width(250.0)
.min_width(210.0)
.show(ctx, |ui| {
ui.heading("Libraries");
ui.small("Package detection ≠ verified compilation ≠ visual editing");
ui.separator();
egui::ScrollArea::vertical().show(ui, |ui| {
for item in &self.package_statuses {
let state = if item.installed {
"Installed"
} else {
"Missing"
};
ui.strong(format!("{} — {state}", item.manifest.display_name));
if !item.missing_files.is_empty() {
ui.small(format!("Needed: {}", item.missing_files.join(", ")));
}
ui.small("Visual editing: not yet implemented");
ui.separator();
}
});
egui::ScrollArea::vertical()
.auto_shrink([false, false])
.show(ui, |ui| self.show_authoring_inspector(ui));
});
}
if self.show_log {
Expand Down Expand Up @@ -982,6 +1049,7 @@ impl eframe::App for TikzariumApp {
&& ui.button(&label).clicked()
{
self.jump_to_line = Some(line);
self.highlight_generated_line = false;
if !self.workspace.show_source() {
self.workspace = WorkspaceMode::Split;
}
Expand Down
Loading
Loading