Skip to main content

max / makenotwork

Describe the project Content panel, closing N12's round-trip filter B4's centrepiece and the close condition makeover-layout N12 had been waiting on: the ticks are a payload the renderer assembles, and the three filter controls are a server round trip rather than a client-side pass over rendered rows. src/quasi/project_content.rs holds the whole panel. A View is the address - search, status, type, sort, direction, open bundles, select-all - so every narrowing tab-project-content.js kept in a closure and re-applied after each swap is a query param that survives one. The bulk bar is the shipped five verbs through Act::over, with Set Price and Add Tag using Act::asking, which is the member that was grown off this exact bar. Inline rename becomes the same shape. Nothing was added to quasi or makeover-layout. Not mounted by quasi: the address carries the project slug and a nest strips its prefix, so the existing axum route serves both renderings and reads QUASI_SCREENS per request. The described writes are registered only under the switch and each answers the panel it happened in. A narrowed panel answers without an ETag: the tag is the project's cache generation and does not move when a filter does. build.rs now writes quasi-selection.js and quasi-clock.js from the renderer's own constants. The shell has named both addresses since S1 and neither file existed, so every page linked scripts that 404ed and the first described bulk bar would have shipped without its count. 23 unit tests, 16 pressed-control integration tests. Parity does not apply to this one and the module says why.
Co-Authored-By
Claude Opus 5 (1M context) <noreply@anthropic.com>
Author: Max Johnson <me@maxj.phd> · 2026-08-21 23:07 UTC
Signed with PGP, not checked
Commit: a52ec579b8a86a3c39ae9d322c669985e78115cd
Parent: 752d1ca
14 files changed, +2224 insertions, -23 deletions
M .gitignore +2
@@ -10,6 +10,8 @@
10 10
11 11 # Generated stylesheets (makeover-build emits these from makeover-geometry and
12 12 # makeover-webview; the crates are the source, a checked-in copy would drift)
13 + server/static/quasi-selection.js
14 + server/static/quasi-clock.js
13 15 server/static/geometry.css
14 16 server/static/layout.css
15 17 server/static/embed-geometry.css
M server/Cargo.lock +20 -20
@@ -5396,11 +5396,11 @@
5396 5396
5397 5397 [[package]]
5398 5398 name = "makeover-layout"
5399 - version = "0.31.0"
5399 + version = "0.32.0"
5400 5400
5401 5401 [[package]]
5402 5402 name = "makeover-touch"
5403 - version = "0.22.0"
5403 + version = "0.22.1"
5404 5404 dependencies = [
5405 5405 "makeover-geometry",
5406 5406 "makeover-layout",
@@ -5408,7 +5408,7 @@
5408 5408
5409 5409 [[package]]
5410 5410 name = "makeover-webview"
5411 - version = "0.53.0"
5411 + version = "0.54.0"
5412 5412 dependencies = [
5413 5413 "makeover-geometry",
5414 5414 "makeover-layout",
@@ -6609,7 +6609,7 @@
6609 6609
6610 6610 [[package]]
6611 6611 name = "quasi-axum"
6612 - version = "0.45.0"
6612 + version = "0.46.0"
6613 6613 dependencies = [
6614 6614 "axum",
6615 6615 "http 1.4.2",
@@ -6620,7 +6620,7 @@
6620 6620
6621 6621 [[package]]
6622 6622 name = "quasi-basics"
6623 - version = "0.45.0"
6623 + version = "0.46.0"
6624 6624 dependencies = [
6625 6625 "makeover-layout",
6626 6626 "quasi-router",
@@ -6628,7 +6628,7 @@
6628 6628
6629 6629 [[package]]
6630 6630 name = "quasi-http"
6631 - version = "0.45.0"
6631 + version = "0.46.0"
6632 6632 dependencies = [
6633 6633 "form_urlencoded",
6634 6634 "http 1.4.2",
@@ -6637,7 +6637,7 @@
6637 6637
6638 6638 [[package]]
6639 6639 name = "quasi-router"
6640 - version = "0.45.0"
6640 + version = "0.46.0"
6641 6641 dependencies = [
6642 6642 "makeover-layout",
6643 6643 ]
@@ -6660,7 +6660,7 @@
6660 6660
6661 6661 [[package]]
6662 6662 name = "quasi-webview"
6663 - version = "0.45.0"
6663 + version = "0.46.0"
6664 6664 dependencies = [
6665 6665 "docengine",
6666 6666 "makeover-layout",
@@ -10734,7 +10734,7 @@
10734 10734
10735 10735 [[patch.unused]]
10736 10736 name = "synckit-client"
10737 - version = "0.8.0"
10737 + version = "0.8.1"
10738 10738
10739 10739 [[patch.unused]]
10740 10740 name = "synckit-config"
@@ -10742,11 +10742,11 @@
10742 10742
10743 10743 [[patch.unused]]
10744 10744 name = "quasi-immediate"
10745 - version = "0.45.0"
10745 + version = "0.46.0"
10746 10746
10747 10747 [[patch.unused]]
10748 10748 name = "quasi-notifs"
10749 - version = "0.45.0"
10749 + version = "0.46.0"
10750 10750
10751 10751 [[patch.unused]]
10752 10752 name = "quasi-store"
@@ -10754,15 +10754,7 @@
10754 10754
10755 10755 [[patch.unused]]
10756 10756 name = "quasi-tauri"
10757 - version = "0.45.0"
10758 -
10759 - [[patch.unused]]
10760 - name = "makeover-immediate"
10761 - version = "0.30.0"
10762 -
10763 - [[patch.unused]]
10764 - name = "makeover-tui"
10765 - version = "0.30.0"
10757 + version = "0.46.0"
10766 10758
10767 10759 [[patch.unused]]
10768 10760 name = "kberg"
@@ -10775,3 +10767,11 @@
10775 10767 [[patch.unused]]
10776 10768 name = "painhours"
10777 10769 version = "0.1.0"
10770 +
10771 + [[patch.unused]]
10772 + name = "makeover-immediate"
10773 + version = "0.31.0"
10774 +
10775 + [[patch.unused]]
10776 + name = "makeover-tui"
10777 + version = "0.31.0"
@@ -266,6 +266,14 @@
266 266 # two Quasi Monos. Under the tree's `[patch]` block the rev is ignored and the
267 267 # working copy is what builds.
268 268 quasi-type = { git = "https://makenot.work/git/max/quasi-type.git", version = "0.1", rev = "e40330602e68fbdd2a9d2917bdec33a7ce573a1f" }
269 + # Writes the two scripts the renderer ships into `static/`, from the constants
270 + # the crate carries. They are the renderer's behaviour rather than the app's --
271 + # a bulk bar's count, a readout that keeps ticking -- and the shell has always
272 + # named their addresses, so without this the head links two files that 404.
273 + # Written from the crate rather than copied into the repo for the reason the
274 + # constants exist: a stale copy in a static directory is a silently dead bulk
275 + # bar, and the version has to be the one the emitter agrees with.
276 + quasi-webview = { git = "https://makenot.work/git/max/quasi.git", version = "0.46" }
269 277
270 278 [dev-dependencies]
271 279 tower = { version = "0.5.3", features = ["util"] }
@@ -21,6 +21,23 @@
21 21 // No explicit touch selector: density hangs off (hover: none),
22 22 // (pointer: coarse) alone, because the server has no mode class to key it
23 23 // on. GO passes `.ui-mode-mobile` because it has one.
24 + // The two scripts quasi-webview ships, written where the shell already says
25 + // they are served from. Both are behaviour a description cannot state and a
26 + // renderer therefore owns: how many rows are ticked and that a control over
27 + // none of them should not be pressable (`quasi-selection.js`), and a readout
28 + // reckoned against the current time that keeps moving (`quasi-clock.js`).
29 + // `Shell::under("/static")` has named both addresses since S1 and neither
30 + // file existed, so every page linked two scripts that 404ed and the first
31 + // described bulk bar would have shipped without its count.
32 + //
33 + // Written from the crate's own constants for the reason they are constants:
34 + // the script reads hooks the emitter writes, so a copy checked in here goes
35 + // stale against the next bump in silence. Gitignored, like the stylesheets.
36 + fs::write("static/quasi-selection.js", quasi_webview::SELECTION_JS)
37 + .expect("write static/quasi-selection.js");
38 + fs::write("static/quasi-clock.js", quasi_webview::CLOCK_JS)
39 + .expect("write static/quasi-clock.js");
40 +
24 41 makeover_build::geometry_css("static/geometry.css", None);
25 42 makeover_build::layout_css("static/layout.css", &makeover_build::Emit::default());
26 43
@@ -37,6 +37,7 @@
37 37 pub mod item_tabs;
38 38 pub mod library_contacts;
39 39 pub mod library_tabs;
40 + pub mod project_content;
40 41 pub mod project_tabs;
41 42 pub mod rich_field;
42 43 pub mod settings_tabs;
@@ -90,6 +90,7 @@
90 90 mod payment_crash_recovery;
91 91 mod postmark;
92 92 mod preferences;
93 + mod project_content_panel;
93 94 mod project_management;
94 95 mod project_sections;
95 96 mod promo_codes_checkout;
@@ -30,7 +30,7 @@
30 30 mod guest_checkout;
31 31 mod imports;
32 32 pub(crate) mod internal;
33 - mod items;
33 + pub(crate) mod items;
34 34 pub(crate) mod license_keys;
35 35 mod links;
36 36 mod passkeys;
@@ -14,7 +14,11 @@
14 14
15 15 use super::super::verify_project_ownership;
16 16
17 - const BULK_ITEM_LIMIT: usize = 100;
17 + /// How many items one bulk write may name.
18 + ///
19 + /// `pub(crate)` since 2026-08-21: the described Content panel's own bulk route
20 + /// enforces the same ceiling, and two copies of it would be two ceilings.
21 + pub(crate) const BULK_ITEM_LIMIT: usize = 100;
18 22
19 23 /// Form input for bulk item operations.
20 24 ///
@@ -9,6 +9,7 @@
9 9 mod tags;
10 10 mod versions;
11 11
12 + pub(crate) use bulk::BULK_ITEM_LIMIT;
12 13 pub(super) use bulk::{bulk_delete, bulk_price, bulk_publish, bulk_tag, bulk_unpublish};
13 14 pub(super) use bundles::{bundle_add, bundle_create_child, bundle_remove, bundle_toggle_listed};
14 15 pub(super) use chapters::{create_chapter, delete_chapter, list_chapters, update_chapter};
@@ -255,6 +255,19 @@
255 255 // hash the page's JS has to act on. `6b24f2df`.
256 256 let shown = quasi::project_tabs::shown_at(query.tab.as_deref(), git_enabled, synckit_enabled);
257 257 let panel = match quasi::project_tabs::route_at(shown, git_enabled, synckit_enabled) {
258 + // The described panel goes in without the region wrapper its route
259 + // answers with: the strip draws that div, and two elements carrying one
260 + // id is a target nothing can aim at.
261 + "content" if config.quasi_screens.enabled(quasi::project_content::SCREEN) => {
262 + let content = project_tabs::build_content(&db, &session_user, &db_project).await?;
263 + Ok(quasi::project_content::fill(
264 + db_project.slug.as_ref(),
265 + &content.items,
266 + &content.deleted_items,
267 + &content.posts,
268 + &quasi::project_content::View::default(),
269 + ))
270 + }
258 271 "content" => project_tabs::build_content(&db, &session_user, &db_project)
259 272 .await?
260 273 .render(),