atproto Thingiverse but good
10

Configure Feed

Select the types of activity you want to include in your feed.

misc cleanup, fixing migrations

Orual (Jun 29, 2026, 7:06 PM EDT) 63584366 4f9fe3db

+233 -503
+1
.gitignore
··· 20 20 rustc-ice-*.txt 21 21 # Impeccable design detector scratch (ignore lists, design sidecar, critique snapshots). 22 22 .impeccable/ 23 + **.png
+1 -1
.polytoken/subagents/code-reviewer.md
··· 3 3 description: Adversarial reviewer for implementation plans, code changes, tests, and shipping quality. Use before handoff, after implementation, or when review gates require independent scrutiny. 4 4 polytoken: 5 5 model: default_model:full 6 - tools: [file_read, grep, glob, shell_exec, skill, mcp__atlassian] 6 + tools: [file_read, grep, glob, shell_exec, skill, mcp__atlassian, mcp__playwright] 7 7 undeferred_tools: [file_read, grep, glob] 8 8 skills_allow: ["reviewing-code-for-shipping-quality", "polytoken:investigating-a-codebase"] 9 9 ---
+144
.polytoken/subagents/playwright-explorer.md
··· 1 + --- 2 + name: playwright-explorer 3 + description: Use when exploring websites, proving hypotheses about web application behavior, automating browser interactions, or generating E2E tests - investigates page structure through accessibility snapshots, tests assumptions systematically, and coordinates Playwright MCP tools with tenacity to complete complex multi-step investigations 4 + polytoken: 5 + model: codex/gpt-5.4-mini(high) 6 + tools: [file_read, grep, glob, skill, mcp__atlassian, mcp__playwright] 7 + undeferred_tools: [file_read, grep, glob] 8 + skills_allow: ["frontend-design", "polytoken:investigating-a-codebase"] 9 + --- 10 + <!-- Derived from ed3d-playwright by Ed Ropple. CC BY-SA 4.0. --> 11 + 12 + # Playwright Controller Agent 13 + 14 + You are a browser exploration and automation agent using Playwright MCP. You work like a detective: forming hypotheses about page structure, testing assumptions, recovering from failures, and persisting until you complete investigations or prove approaches. 15 + 16 + ## Core Philosophy: Structure Over Pixels 17 + 18 + Playwright MCP is designed for **LLM-driven browser interaction** using structured data, not screenshots. 19 + 20 + **Critical distinction:** 21 + - **`browser_snapshot`** (PRIMARY): Returns accessibility tree with roles, labels, semantic structure 22 + - Smallest context footprint 23 + - Deterministic for element selection 24 + - Shows page structure clearly 25 + 26 + - **`browser_take_screenshot`** (FALLBACK): Returns image 27 + - Use ONLY for visual verification (CSS, layout, colours) 28 + - Larger context usage 29 + - Debugging tool, not primary inspection method 30 + 31 + **Rule:** Always use `browser_snapshot` first. Only use `browser_take_screenshot` when you need visual confirmation. 32 + 33 + ## MCP Server Configuration 34 + 35 + These tools come from the `playwright` MCP server. If directed to use a different MCP server, use those tools instead. 36 + 37 + The default configuration uses: 38 + - `@playwright/mcp` (Microsoft official) 39 + - `--isolated` mode (clean profile per session) 40 + - `--caps=vision` (coordinate-based interaction when needed) 41 + - On NixOS: nix-patched browser binaries (no FHS needed) 42 + 43 + ## Available Playwright MCP Tools 44 + 45 + ### Navigation 46 + - `browser_navigate` - Navigate to URLs 47 + - `browser_navigate_back` / `browser_navigate_forward` - History navigation 48 + 49 + ### Data Extraction & Inspection (PRIMARY TOOLS) 50 + - `browser_snapshot` - **Capture accessibility snapshot (USE THIS FIRST)** 51 + - `browser_take_screenshot` - Capture visual screenshot (fallback only) 52 + - `browser_console_messages` - Get console errors and warnings 53 + - `browser_network_requests` - Inspect network requests 54 + 55 + ### Element Interaction 56 + - `browser_click` - Click elements using accessibility-based selection 57 + - `browser_drag` - Drag and drop between elements 58 + - `browser_type` - Type text into editable elements 59 + - `browser_fill_form` - Fill multiple form fields at once 60 + - `browser_select_option` - Select dropdown options 61 + - `browser_hover` - Hover over elements 62 + - `browser_press_key` - Press keyboard keys 63 + - `browser_file_upload` - Upload files 64 + - `browser_handle_dialog` - Accept/dismiss browser dialogs 65 + 66 + ### Tab Management 67 + - `browser_tabs` - List, create, close, or select browser tabs 68 + 69 + ### Evaluation & Verification 70 + - `browser_evaluate` - Execute JavaScript in page context 71 + - `browser_run_code` - Run complete Playwright code snippets 72 + - `browser_wait_for` - Wait for text or time duration 73 + - `browser_verify_element_visible` / `browser_verify_text_visible` / `browser_verify_value` 74 + 75 + ### Advanced (opt-in capabilities) 76 + - `browser_pdf_save` (requires `--caps=pdf`) 77 + - `browser_generate_locator` (requires `--caps=testing`) 78 + - `browser_start_tracing` / `browser_stop_tracing` (requires `--caps=tracing`) 79 + - `browser_mouse_click_xy` / `browser_mouse_drag_xy` / `browser_mouse_move_xy` (requires `--caps=vision`) 80 + - `browser_resize` - Resize browser viewport 81 + 82 + ## Investigation-Driven Approach 83 + 84 + ### Pattern: Prove Before You Perform 85 + 86 + 1. **Form hypothesis** about page structure 87 + 2. **Take browser_snapshot** and examine the accessibility tree 88 + 3. **Verify** your hypothesis against reality 89 + 4. **If confirmed**: proceed with confidence 90 + 5. **If wrong**: reformulate based on what you actually see 91 + 92 + ### Pattern: Graceful Degradation 93 + 94 + When an approach fails: 95 + 1. Take `browser_snapshot` to see actual page state 96 + 2. Check `browser_console_messages` for JS errors 97 + 3. Check `browser_network_requests` for failed API calls 98 + 4. Try alternative selector strategy (role → label → testid → CSS) 99 + 5. Try alternative interaction method (keyboard vs click) 100 + 6. Report findings with evidence 101 + 102 + ### Pattern: Multi-Step Persistence 103 + 104 + 1. Break goal into verifiable sub-steps 105 + 2. Test each step independently 106 + 3. Verify assumptions at each transition 107 + 4. Handle errors at each step with specific recovery 108 + 5. Document discoveries along the way 109 + 110 + ## Context Minimisation Strategy 111 + 112 + ### Snapshot-first inspection — always 113 + ### Targeted evaluation — extract specific data, not entire DOM 114 + ### Batch operations — combine multiple queries into one `browser_evaluate` call 115 + ### Network & console for debugging — not guesswork 116 + 117 + ## Locator Strategy (Most Resilient First) 118 + 119 + 1. Test ID: `page.getByTestId('submit-button')` 120 + 2. Role + Name: `page.getByRole('button', { name: 'Submit' })` 121 + 3. Label: `page.getByLabel('Email address')` 122 + 4. CSS Selector (last resort): `page.locator('.submit-btn')` 123 + 124 + ## Reporting Format 125 + 126 + **Investigation Goal:** [What you were asked to do] 127 + **Approach:** [Hypotheses tested] 128 + **Findings:** [What you discovered with evidence] 129 + **Actions Taken:** [Specific tools used] 130 + **URL(s):** [Current page URL(s)] 131 + **Status:** [Success/Partial/Failed] 132 + **Data Extracted:** [Structured data or summary] 133 + **Issues Encountered:** [Problems and how you handled them] 134 + **Next Steps:** [Recommendations or follow-up] 135 + 136 + ## Remember 137 + 138 + You are an **investigator**: 139 + 1. Form hypotheses about page structure and behaviour 140 + 2. Test assumptions with snapshots and small experiments 141 + 3. Adapt when wrong — errors are data 142 + 4. Persist through obstacles 143 + 5. Minimise context — snapshots over screenshots, batch evaluations 144 + 6. Generate value — translate explorations into test code when useful
+82 -22
migrations/001_projection_schema.sql migrations/0001_initial_schema.sql
··· 1 - -- Polymodel SQLite projection schema. 2 - -- Derived views projected from Hydrant ATProto events for space.polymodel.* collections. 1 + -- Polymodel initial SQLite projection schema (consolidated baseline). 2 + -- 3 + -- Single non-destructive baseline combining the former 001–005 migrations into 4 + -- their final form: record_json on content tables, persisted CIDs on social 5 + -- records, owner-scoped upload staging, and read-your-writes pending-ops. Fresh 6 + -- databases get the complete schema in one step, and future schema changes add 7 + -- new migration files rather than rewriting tables, so projected data is never 8 + -- destroyed by a migration. 3 9 -- 4 - -- NOTE: WAL mode is intentionally NOT set here. sqlx runs migrations inside a 5 - -- transaction and SQLite silently ignores journal_mode changes within one. 6 - -- WAL is enabled programmatically on the connection pool (see src/indexing/db.rs). 10 + -- WAL mode is intentionally NOT set here: sqlx runs migrations inside a 11 + -- transaction and SQLite silently ignores journal_mode changes within one. WAL 12 + -- is enabled programmatically on the connection pool (src/indexing/db.rs). 7 13 8 14 -- Projection state (durable cursor for crash recovery) 9 15 CREATE TABLE projection_state ( ··· 12 18 updated_at INTEGER NOT NULL 13 19 ); 14 20 15 - -- Derived content views 21 + -- Content records (space.polymodel.library.*). record_json holds the raw ATProto 22 + -- record so read endpoints and eager read-your-own-writes resolve from SQLite. 16 23 CREATE TABLE things ( 17 24 did TEXT NOT NULL, 18 25 rkey TEXT NOT NULL, ··· 26 33 instructions_text TEXT, 27 34 cover_json TEXT, 28 35 derived_from_uri TEXT, 36 + record_json TEXT, 29 37 created_at INTEGER NOT NULL, 30 38 indexed_at INTEGER NOT NULL, 31 39 PRIMARY KEY (did, rkey) ··· 40 48 cid TEXT NOT NULL, 41 49 name TEXT NOT NULL, 42 50 summary TEXT, 51 + record_json TEXT, 43 52 created_at INTEGER NOT NULL, 44 53 indexed_at INTEGER NOT NULL, 45 54 PRIMARY KEY (did, rkey) ··· 53 62 name TEXT NOT NULL, 54 63 format TEXT, 55 64 file_json TEXT NOT NULL, 65 + record_json TEXT, 56 66 created_at INTEGER NOT NULL, 57 67 indexed_at INTEGER NOT NULL, 58 68 PRIMARY KEY (did, rkey) ··· 77 87 CREATE INDEX idx_model_parts_part ON model_parts(part_uri); 78 88 CREATE INDEX idx_model_parts_model_pos ON model_parts(model_uri, position); 79 89 80 - -- Social record projections 90 + -- Social record projections (space.polymodel.graph.*), with persisted record CIDs. 81 91 CREATE TABLE likes ( 82 92 did TEXT NOT NULL, 83 93 rkey TEXT NOT NULL, 94 + cid TEXT NOT NULL, 84 95 subject_uri TEXT NOT NULL, 85 96 created_at INTEGER NOT NULL, 86 97 PRIMARY KEY (did, rkey), ··· 91 102 CREATE TABLE saves ( 92 103 did TEXT NOT NULL, 93 104 rkey TEXT NOT NULL, 105 + cid TEXT NOT NULL, 94 106 subject_uri TEXT NOT NULL, 95 107 note TEXT, 96 108 created_at INTEGER NOT NULL, ··· 102 114 CREATE TABLE tags ( 103 115 did TEXT NOT NULL, 104 116 rkey TEXT NOT NULL, 117 + cid TEXT NOT NULL, 105 118 subject_uri TEXT NOT NULL, 106 119 tag TEXT NOT NULL, 107 120 created_at INTEGER NOT NULL, ··· 113 126 CREATE TABLE listitems ( 114 127 did TEXT NOT NULL, 115 128 rkey TEXT NOT NULL, 129 + cid TEXT NOT NULL, 116 130 list_uri TEXT NOT NULL, 117 131 subject_uri TEXT NOT NULL, 118 132 created_at INTEGER NOT NULL, ··· 120 134 ); 121 135 CREATE INDEX idx_listitems_list ON listitems(list_uri, created_at); 122 136 123 - -- Denormalized counts 137 + -- Denormalized engagement counts 124 138 CREATE TABLE content_stats ( 125 139 uri TEXT PRIMARY KEY, 126 140 like_count INTEGER NOT NULL DEFAULT 0, ··· 128 142 tag_count INTEGER NOT NULL DEFAULT 0 129 143 ); 130 144 131 - -- Full-text search (FTS5, external content) 145 + -- Full-text search (FTS5, external content) over things 132 146 CREATE VIRTUAL TABLE things_fts USING fts5( 133 147 name, summary, tags_text, instructions_text, 134 148 content=things, content_rowid=rowid, ··· 150 164 VALUES (new.rowid, new.name, new.summary, new.tags_text, new.instructions_text); 151 165 END; 152 166 167 + -- Polymodel actor profile (singleton record, rkey = "self"). Handle is not stored 168 + -- here: it lives in `identities` (identity-level) so it stays current across 169 + -- handle changes. 170 + CREATE TABLE profiles ( 171 + did TEXT PRIMARY KEY, 172 + display_name TEXT, 173 + description TEXT, 174 + avatar_json TEXT, 175 + default_license TEXT, 176 + pronouns TEXT, 177 + printers_json TEXT, 178 + links_json TEXT, 179 + record_json TEXT NOT NULL, 180 + indexed_at INTEGER NOT NULL 181 + ); 182 + 183 + -- DID -> handle, refreshed by identity events. Joined by profile/actor hydration. 184 + CREATE TABLE identities ( 185 + did TEXT PRIMARY KEY, 186 + handle TEXT, 187 + updated_at INTEGER NOT NULL 188 + ); 189 + 153 190 -- OAuth session persistence (DB-backed ClientAuthStore for Jacquard 0.12 OAuth). 154 - -- 155 - -- Each row mirrors the durable surfaces of `jacquard::oauth::session::ClientSessionData`: 156 - -- the structured columns (account_did, session_id, endpoints, timestamps) power 157 - -- key enumeration, by-DID/any selection, and deletes *without* deserializing the 158 - -- payload, while `payload` holds the canonical JSON serialization of the full 159 - -- `ClientSessionData` (token set, DPoP key + nonces, scopes). DPoP nonces are 160 - -- persisted here, not in an ad-hoc store; the in-process `SessionRegistry` 161 - -- mutex is concurrency-only and not durable. 191 + -- Structured columns power key enumeration / by-DID selection / deletes without 192 + -- deserializing payload; payload holds the canonical ClientSessionData JSON. 162 193 CREATE TABLE oauth_sessions ( 163 194 account_did TEXT NOT NULL, 164 195 session_id TEXT NOT NULL, ··· 173 204 ); 174 205 CREATE INDEX idx_oauth_sessions_did ON oauth_sessions(account_did); 175 206 176 - -- Pending OAuth authorization requests (PAR `state` round-trip). 177 - -- `payload` holds the canonical JSON serialization of 178 - -- `jacquard::oauth::session::AuthRequestData` (PKCE verifier, DPoP key + nonce, 179 - -- request_uri). `account_did` is nullable because the initiating identifier is 180 - -- not always known when the request is created. 207 + -- Pending OAuth authorization requests (PAR state round-trip). 181 208 CREATE TABLE oauth_auth_requests ( 182 209 state TEXT NOT NULL, 183 210 authserver_url TEXT NOT NULL, ··· 190 217 PRIMARY KEY (state) 191 218 ); 192 219 CREATE INDEX idx_oauth_auth_req_did ON oauth_auth_requests(account_did); 220 + 221 + -- Owner-scoped upload staging for raw geometry bytes (publish flow). 222 + CREATE TABLE upload_staging ( 223 + owner_did TEXT NOT NULL, 224 + upload_id TEXT NOT NULL, 225 + sha256 TEXT NOT NULL, 226 + mime_type TEXT NOT NULL, 227 + size INTEGER NOT NULL, 228 + filename TEXT, 229 + status TEXT NOT NULL, 230 + file_json TEXT NOT NULL, 231 + chunks_json TEXT NOT NULL, 232 + created_at INTEGER NOT NULL, 233 + updated_at INTEGER NOT NULL, 234 + PRIMARY KEY (owner_did, upload_id) 235 + ); 236 + CREATE INDEX idx_upload_staging_owner_updated 237 + ON upload_staging(owner_did, updated_at DESC); 238 + 239 + -- Read-your-writes pending-ops tracking. Collection-agnostic; keyed by record 240 + -- URI. Cleared by the firehose record-event projection once the Hydrant #commit 241 + -- redelivers the event. 242 + CREATE TABLE pending_writes ( 243 + uri TEXT PRIMARY KEY, 244 + cid TEXT NOT NULL, 245 + value TEXT NOT NULL, 246 + written_at INTEGER NOT NULL 247 + ); 248 + 249 + CREATE TABLE pending_deletes ( 250 + uri TEXT PRIMARY KEY, 251 + deleted_at INTEGER NOT NULL 252 + );
-41
migrations/002_appview_records_profiles_identities.sql
··· 1 - -- PM-26: appview read support. 2 - -- 3 - -- (a) record_json: the raw ATProto record for each content row, so read endpoints 4 - -- can populate the required `record` field on thingView/modelView/partView 5 - -- directly from SQLite. This also gives the write path (PM-28) read-your-own- 6 - -- write capability: Hydrant exposes no public single-record write API into 7 - -- its fjall store, so the record must live in SQLite to be readable 8 - -- immediately after an eager write projection. 9 - -- (b) profiles: denormalized space.polymodel.actor.profile projection. 10 - -- (c) identities: DID -> handle map kept fresh by identity events, so handle 11 - -- changes are caught for profile/actor hydration. The projection previously 12 - -- filtered out non-record events, which meant handle changes were never 13 - -- reflected (records do not carry the actor's current handle). 14 - 15 - ALTER TABLE things ADD COLUMN record_json TEXT; 16 - ALTER TABLE models ADD COLUMN record_json TEXT; 17 - ALTER TABLE parts ADD COLUMN record_json TEXT; 18 - 19 - -- Polymodel actor profile (singleton record, rkey = "self"). Denormalized fields 20 - -- for the profileView union variant plus the raw record for the required `record` 21 - -- field. Handle is not stored here: it lives in `identities` (identity-level, not 22 - -- part of the profile record) so it stays current across handle changes. 23 - CREATE TABLE profiles ( 24 - did TEXT PRIMARY KEY, 25 - display_name TEXT, 26 - description TEXT, 27 - avatar_json TEXT, 28 - default_license TEXT, 29 - pronouns TEXT, 30 - printers_json TEXT, 31 - links_json TEXT, 32 - record_json TEXT NOT NULL, 33 - indexed_at INTEGER NOT NULL 34 - ); 35 - 36 - -- DID -> handle, refreshed by identity events. Joined by profile/actor hydration. 37 - CREATE TABLE identities ( 38 - did TEXT PRIMARY KEY, 39 - handle TEXT, 40 - updated_at INTEGER NOT NULL 41 - );
-21
migrations/003_upload_staging.sql
··· 1 - -- PM-28: owner-scoped upload staging for raw geometry bytes. 2 - -- 3 - -- stageFile writes blobs to the authenticated actor's PDS first, then stores the 4 - -- reassembly metadata needed by publishThing to build space.polymodel.library.defs#file. 5 - CREATE TABLE upload_staging ( 6 - owner_did TEXT NOT NULL, 7 - upload_id TEXT NOT NULL, 8 - sha256 TEXT NOT NULL, 9 - mime_type TEXT NOT NULL, 10 - size INTEGER NOT NULL, 11 - filename TEXT, 12 - status TEXT NOT NULL, 13 - file_json TEXT NOT NULL, 14 - chunks_json TEXT NOT NULL, 15 - created_at INTEGER NOT NULL, 16 - updated_at INTEGER NOT NULL, 17 - PRIMARY KEY (owner_did, upload_id) 18 - ); 19 - 20 - CREATE INDEX idx_upload_staging_owner_updated 21 - ON upload_staging(owner_did, updated_at DESC);
-56
migrations/004_social_record_cids.sql
··· 1 - -- Store CIDs on all social record projection tables. Existing rows cannot be 2 - -- backfilled because previous schema versions did not store record CIDs; rebuild 3 - -- these derived tables instead of introducing placeholder CIDs. Hydrant/firehose 4 - -- can replay social records, and eager writes after this migration preserve CIDs. 5 - 6 - DROP TABLE likes; 7 - CREATE TABLE likes ( 8 - did TEXT NOT NULL, 9 - rkey TEXT NOT NULL, 10 - cid TEXT NOT NULL, 11 - subject_uri TEXT NOT NULL, 12 - created_at INTEGER NOT NULL, 13 - PRIMARY KEY (did, rkey), 14 - UNIQUE (did, subject_uri) 15 - ); 16 - CREATE INDEX idx_likes_subject ON likes(subject_uri); 17 - 18 - DROP TABLE saves; 19 - CREATE TABLE saves ( 20 - did TEXT NOT NULL, 21 - rkey TEXT NOT NULL, 22 - cid TEXT NOT NULL, 23 - subject_uri TEXT NOT NULL, 24 - note TEXT, 25 - created_at INTEGER NOT NULL, 26 - PRIMARY KEY (did, rkey), 27 - UNIQUE (did, subject_uri) 28 - ); 29 - CREATE INDEX idx_saves_subject ON saves(subject_uri); 30 - 31 - DROP TABLE tags; 32 - CREATE TABLE tags ( 33 - did TEXT NOT NULL, 34 - rkey TEXT NOT NULL, 35 - cid TEXT NOT NULL, 36 - subject_uri TEXT NOT NULL, 37 - tag TEXT NOT NULL, 38 - created_at INTEGER NOT NULL, 39 - PRIMARY KEY (did, rkey) 40 - ); 41 - CREATE INDEX idx_tags_subject ON tags(subject_uri); 42 - CREATE INDEX idx_tags_tag ON tags(tag); 43 - 44 - DROP TABLE listitems; 45 - CREATE TABLE listitems ( 46 - did TEXT NOT NULL, 47 - rkey TEXT NOT NULL, 48 - cid TEXT NOT NULL, 49 - list_uri TEXT NOT NULL, 50 - subject_uri TEXT NOT NULL, 51 - created_at INTEGER NOT NULL, 52 - PRIMARY KEY (did, rkey) 53 - ); 54 - CREATE INDEX idx_listitems_list ON listitems(list_uri, created_at); 55 - 56 - UPDATE content_stats SET like_count = 0, save_count = 0, tag_count = 0;
-30
migrations/005_pending_ops.sql
··· 1 - -- Read-your-writes pending-ops tracking (PM-47). 2 - -- 3 - -- Eager local writes/deletes — both PM-28 mediated writes and the PM-47 4 - -- passthrough proxy — mark a record "pending" until the Hydrant firehose 5 - -- re-delivers the same event, at which point the PDS/AppView is authoritative 6 - -- again. `repo.getRecord` of a `space.polymodel.*` record consults these tables 7 - -- to reflect a not-yet-confirmed local op: 8 - -- * a pending write whose cid differs from the PDS returns the local value; 9 - -- * a pending delete returns RecordNotFound even if a lagging source still 10 - -- serves the record. 11 - -- 12 - -- These tables are collection-agnostic (keyed by record URI); they do not touch 13 - -- the per-collection projection tables. The matching row is cleared by the 14 - -- firehose record-event projection path once the Hydrant `#commit` redelivers 15 - -- the event. 16 - 17 - CREATE TABLE pending_writes ( 18 - -- Canonical record URI: at://{did}/{collection}/{rkey}. 19 - uri TEXT PRIMARY KEY, 20 - cid TEXT NOT NULL, 21 - -- The raw record JSON, so repo.getRecord can rebuild `value` without a 22 - -- collection-specific lookup. 23 - value TEXT NOT NULL, 24 - written_at INTEGER NOT NULL 25 - ); 26 - 27 - CREATE TABLE pending_deletes ( 28 - uri TEXT PRIMARY KEY, 29 - deleted_at INTEGER NOT NULL 30 - );
pm39-home-light-desktop.png

This is a binary file and will not be displayed.

pm39-home-light.png

This is a binary file and will not be displayed.

pm41-current-after-viewer.png

This is a binary file and will not be displayed.

-156
pm41-detail-snapshot.md
··· 1 - - generic [active] [ref=e1] [box=0,0,1280,3874]: 2 - - generic [ref=e5] [cursor=pointer] [box=939,10,331,65]: 3 - - generic [ref=e6] [box=947,18,315,22]: 4 - - img [ref=e7] [box=947,22,18,18] 5 - - heading "Your app is being rebuilt." [level=3] [ref=e23] [box=970,18,177,22] 6 - - paragraph [ref=e12] [box=947,50,315,17]: A non-hot-reloadable change occurred and we must rebuild. 7 - - main [ref=e14] [box=0,0,1280,3874]: 8 - - navigation "Thing detail navigation" [ref=e15] [box=0,0,1280,25]: 9 - - link "← Back to browse" [ref=e16] [cursor=pointer] [box=0,2,134,20]: 10 - - /url: / 11 - - generic [ref=e24] [box=0,25,1280,349]: 12 - - generic [ref=e25] [box=0,25,1280,18]: Thing detail 13 - - generic [ref=e26] [box=0,64,1280,310]: 14 - - generic [ref=e27] [box=0,64,1280,96]: 15 - - heading "Parametric enclosure kit" [level=1] [ref=e28] [box=0,64,1280,50] 16 - - paragraph [ref=e29] [box=0,135,1280,25]: by Ari Chen 17 - - generic "Thing stats" [ref=e30] [box=0,176,1280,198]: 18 - - generic [ref=e31] [box=0,176,1280,50]: 19 - - term [ref=e32] [box=0,176,1280,25]: Models 20 - - definition [ref=e33] [box=40,201,1240,25]: 3 models 21 - - generic [ref=e34] [box=0,226,1280,50]: 22 - - term [ref=e35] [box=0,226,1280,25]: Parts 23 - - definition [ref=e36] [box=40,250,1240,25]: 18 parts 24 - - generic [ref=e37] [box=0,275,1280,50]: 25 - - term [ref=e38] [box=0,275,1280,25]: Likes 26 - - definition [ref=e39] [box=40,300,1240,25]: 42 likes 27 - - generic [ref=e40] [box=0,325,1280,50]: 28 - - term [ref=e41] [box=0,325,1280,25]: Saves 29 - - definition [ref=e42] [box=40,349,1240,25]: 12 saves 30 - - region "Thing overview" [ref=e43] [box=0,390,1280,144]: 31 - - img "Rendered preview of a parametric electronics enclosure" [ref=e45] [box=1,391,442,25] 32 - - generic [ref=e46] [box=1,432,1278,101]: 33 - - generic [ref=e47] [box=1,432,1278,67]: 34 - - paragraph [ref=e48] [box=1,432,1278,25]: A configurable electronics enclosure kit with three printable model variants and detailed part files. 35 - - generic "Thing tags and license" [ref=e49] [box=1,473,1278,26]: 36 - - generic [ref=e50] [box=1,474,80,25]: CC-BY-4.0 37 - - generic [ref=e51] [box=81,474,87,25]: "#enclosure" 38 - - generic [ref=e52] [box=168,474,94,25]: "#parametric" 39 - - generic [ref=e53] [box=262,474,122,25]: "#print-in-place" 40 - - generic "Thing actions" [ref=e54] [box=1,499,1278,34]: 41 - - button "Download files" [ref=e55] [cursor=pointer] [box=1,499,136,34] 42 - - button "Save disabled until sign in is available" [disabled] [ref=e56] [cursor=pointer] [box=137,499,61,34]: Save 43 - - button "Remix disabled until publishing is available" [disabled] [ref=e57] [cursor=pointer] [box=197,499,72,34]: Remix 44 - - region "Thing detail sections" [ref=e58] [box=0,534,1280,3340]: 45 - - article [ref=e59] [box=0,534,1280,169]: 46 - - heading "Overview" [level=2] [ref=e61] [box=1,555,1278,37] 47 - - list [ref=e62] [box=1,612,1278,74]: 48 - - listitem [ref=e63] [box=41,612,1238,25]: Print the body and lid in PETG or PLA+ with 0.2 mm layers. 49 - - listitem [ref=e64] [box=41,637,1238,25]: Use the model selector to choose the standard, panel-mount, or ruggedized variant. 50 - - listitem [ref=e65] [box=41,662,1238,25]: Press brass inserts into the corner bosses before final assembly. 51 - - article [ref=e66] [box=0,703,1280,153]: 52 - - heading "Models" [level=2] [ref=e68] [box=1,724,1278,37] 53 - - list "Model variants" [ref=e69] [box=1,781,1278,74]: 54 - - listitem [ref=e70] [cursor=pointer] [box=1,782,806,23]: Snap-fit electronics enclosure6 parts · CC-BY-4.0Main printable enclosure with lid, body, buttons, and mounting hardware. 55 - - listitem [ref=e71] [cursor=pointer] [box=1,807,671,23]: Panel-mount variant6 parts · CC-BY-4.0Alternate front panel for flush mounting sensors and displays. 56 - - listitem [ref=e72] [cursor=pointer] [box=1,832,719,23]: Ruggedized outdoor lid6 parts · CC-BY-4.0Weather-resistant lid and gasket parts for workshop deployments. 57 - - article [ref=e73] [box=0,857,1280,259]: 58 - - heading "Selected model" [level=2] [ref=e75] [box=1,878,1278,37] 59 - - generic [ref=e76] [box=1,935,1278,180]: 60 - - heading "Snap-fit electronics enclosure" [level=3] [ref=e77] [box=1,935,1278,29] 61 - - paragraph [ref=e78] [box=1,982,1278,25]: Main printable enclosure with lid, body, buttons, and mounting hardware. 62 - - list [ref=e79] [box=1,1023,1278,50]: 63 - - listitem [ref=e80] [box=41,1023,1238,25]: Review the ordered part list before printing. 64 - - listitem [ref=e81] [box=41,1048,1238,25]: Print fit-critical clips slowly for best tolerances. 65 - - generic "Selected model tags" [ref=e82] [box=1,1089,1278,26]: 66 - - generic [ref=e83] [box=1,1090,73,25]: "#variant" 67 - - generic [ref=e84] [box=74,1090,87,25]: "#enclosure" 68 - - article [ref=e85] [box=0,1116,1280,2402]: 69 - - heading "Parts & files" [level=2] [ref=e87] [box=1,1137,1278,37] 70 - - list "Ordered part files" [ref=e88] [box=1,1194,1278,2307]: 71 - - listitem [ref=e89] [box=1,1194,1278,371]: 72 - - generic "Part position" [ref=e90] [box=1,1194,1278,25]: "1" 73 - - generic [ref=e91] [box=1,1238,1278,328]: 74 - - heading "Main enclosure body" [level=3] [ref=e92] [box=1,1238,1278,29] 75 - - generic "Part file metadata" [ref=e93] [box=1,1285,1278,149]: 76 - - generic [ref=e94] [box=1,1285,1278,50] 77 - - generic [ref=e97] [box=1,1335,1278,50] 78 - - generic [ref=e100] [box=1,1384,1278,50] 79 - - paragraph [ref=e103] [box=1,1450,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 80 - - list "Print settings" [ref=e104] [box=1,1491,1278,74]: 81 - - listitem [ref=e105] [box=41,1491,1238,25]: 0.2 mm layer height 82 - - listitem [ref=e106] [box=41,1516,1238,25]: 15% gyroid infill 83 - - listitem [ref=e107] [box=41,1540,1238,25]: No supports required unless noted 84 - - listitem [ref=e108] [box=1,1581,1278,371]: 85 - - generic "Part position" [ref=e109] [box=1,1581,1278,25]: "2" 86 - - generic [ref=e110] [box=1,1625,1278,328]: 87 - - heading "Snap-fit lid" [level=3] [ref=e111] [box=1,1625,1278,29] 88 - - generic "Part file metadata" [ref=e112] [box=1,1672,1278,149]: 89 - - generic [ref=e113] [box=1,1672,1278,50] 90 - - generic [ref=e116] [box=1,1722,1278,50] 91 - - generic [ref=e119] [box=1,1772,1278,50] 92 - - paragraph [ref=e122] [box=1,1837,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 93 - - list "Print settings" [ref=e123] [box=1,1878,1278,74]: 94 - - listitem [ref=e124] [box=41,1878,1238,25]: 0.2 mm layer height 95 - - listitem [ref=e125] [box=41,1903,1238,25]: 15% gyroid infill 96 - - listitem [ref=e126] [box=41,1928,1238,25]: No supports required unless noted 97 - - listitem [ref=e127] [box=1,1968,1278,371]: 98 - - generic "Part position" [ref=e128] [box=1,1968,1278,25]: "3" 99 - - generic [ref=e129] [box=1,2012,1278,328]: 100 - - heading "Button cap set" [level=3] [ref=e130] [box=1,2012,1278,29] 101 - - generic "Part file metadata" [ref=e131] [box=1,2060,1278,149]: 102 - - generic [ref=e132] [box=1,2060,1278,50] 103 - - generic [ref=e135] [box=1,2109,1278,50] 104 - - generic [ref=e138] [box=1,2159,1278,50] 105 - - paragraph [ref=e141] [box=1,2224,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 106 - - list "Print settings" [ref=e142] [box=1,2265,1278,74]: 107 - - listitem [ref=e143] [box=41,2265,1238,25]: 0.2 mm layer height 108 - - listitem [ref=e144] [box=41,2290,1238,25]: 15% gyroid infill 109 - - listitem [ref=e145] [box=41,2315,1238,25]: No supports required unless noted 110 - - listitem [ref=e146] [box=1,2356,1278,371]: 111 - - generic "Part position" [ref=e147] [box=1,2356,1278,25]: "4" 112 - - generic [ref=e148] [box=1,2399,1278,328]: 113 - - heading "USB-C bezel" [level=3] [ref=e149] [box=1,2399,1278,29] 114 - - generic "Part file metadata" [ref=e150] [box=1,2447,1278,149]: 115 - - generic [ref=e151] [box=1,2447,1278,50] 116 - - generic [ref=e154] [box=1,2496,1278,50] 117 - - generic [ref=e157] [box=1,2546,1278,50] 118 - - paragraph [ref=e160] [box=1,2612,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 119 - - list "Print settings" [ref=e161] [box=1,2652,1278,74]: 120 - - listitem [ref=e162] [box=41,2652,1238,25]: 0.2 mm layer height 121 - - listitem [ref=e163] [box=41,2677,1238,25]: 15% gyroid infill 122 - - listitem [ref=e164] [box=41,2702,1238,25]: No supports required unless noted 123 - - listitem [ref=e165] [box=1,2743,1278,371]: 124 - - generic "Part position" [ref=e166] [box=1,2743,1278,25]: "5" 125 - - generic [ref=e167] [box=1,2786,1278,328]: 126 - - heading "PCB mounting tray" [level=3] [ref=e168] [box=1,2786,1278,29] 127 - - generic "Part file metadata" [ref=e169] [box=1,2834,1278,149]: 128 - - generic [ref=e170] [box=1,2834,1278,50] 129 - - generic [ref=e173] [box=1,2884,1278,50] 130 - - generic [ref=e176] [box=1,2933,1278,50] 131 - - paragraph [ref=e179] [box=1,2999,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 132 - - list "Print settings" [ref=e180] [box=1,3040,1278,74]: 133 - - listitem [ref=e181] [box=41,3040,1238,25]: 0.2 mm layer height 134 - - listitem [ref=e182] [box=41,3064,1238,25]: 15% gyroid infill 135 - - listitem [ref=e183] [box=41,3089,1238,25]: No supports required unless noted 136 - - listitem [ref=e184] [box=1,3130,1278,371]: 137 - - generic "Part position" [ref=e185] [box=1,3130,1278,25]: "6" 138 - - generic [ref=e186] [box=1,3174,1278,328]: 139 - - heading "Corner screw boss" [level=3] [ref=e187] [box=1,3174,1278,29] 140 - - generic "Part file metadata" [ref=e188] [box=1,3221,1278,149]: 141 - - generic [ref=e189] [box=1,3221,1278,50] 142 - - generic [ref=e192] [box=1,3271,1278,50] 143 - - generic [ref=e195] [box=1,3320,1278,50] 144 - - paragraph [ref=e198] [box=1,3386,1278,25]: Orient the visible face upward and verify first-layer adhesion around clips. 145 - - list "Print settings" [ref=e199] [box=1,3427,1278,74]: 146 - - listitem [ref=e200] [box=41,3427,1238,25]: 0.2 mm layer height 147 - - listitem [ref=e201] [box=41,3452,1238,25]: 15% gyroid infill 148 - - listitem [ref=e202] [box=41,3476,1238,25]: No supports required unless noted 149 - - article [ref=e203] [box=0,3518,1280,211]: 150 - - heading "3D viewer" [level=2] [ref=e205] [box=1,3539,1278,37] 151 - - img "Viewer unavailable placeholder" [ref=e206] [box=1,3596,1278,132]: 152 - - generic [ref=e207] [box=13,3610,142,25]: Viewer unavailable 153 - - paragraph [ref=e208] [box=13,3650,1254,50]: This thing has file metadata from the appview, but no browser-loadable STL asset URL is exposed yet. The page keeps the printable file contract visible while the viewer adapter waits for a resolved asset manifest. 154 - - article [ref=e209] [box=0,3729,1280,145]: 155 - - heading "Community" [level=2] [ref=e211] [box=1,3750,1278,37] 156 - - paragraph [ref=e212] [box=1,3807,1278,50]: Makes, comments, related things, and remix lineage need follow-on appview/write contracts. They are intentionally shown as unavailable instead of pretending live social data exists.
pm41-detail.png

This is a binary file and will not be displayed.

pm41-live-inspect.png

This is a binary file and will not be displayed.

pm41-loaded-after-viewer.png

This is a binary file and will not be displayed.

-175
pm41-loaded-snapshot.md
··· 1 - - generic [active] [ref=e1] [box=0,0,1280,3478]: 2 - - generic [ref=e5] [cursor=pointer] [box=1939,10,331,65]: 3 - - generic [ref=e6] [box=1947,18,315,22]: 4 - - img [ref=e7] [box=1947,22,18,18] 5 - - heading [level=3] [ref=e11] [box=1970,18,177,22]: Your app is being rebuilt. 6 - - paragraph [ref=e12] [box=1947,50,315,17]: A non-hot-reloadable change occurred and we must rebuild. 7 - - main [ref=e14] [box=50,0,1180,3478]: 8 - - navigation "Thing detail navigation" [ref=e15] [box=88,26,1103,25]: 9 - - link "← Back to browse" [ref=e16] [cursor=pointer] [box=88,26,139,25]: 10 - - /url: / 11 - - generic [ref=e23] [box=88,65,1103,111]: 12 - - generic [ref=e24] [box=88,65,1103,18]: Thing detail 13 - - generic [ref=e25] [box=88,91,1103,86]: 14 - - generic [ref=e26] [box=88,91,759,86]: 15 - - heading "Parametric enclosure kit" [level=1] [ref=e27] [box=88,91,759,61] 16 - - paragraph [ref=e28] [box=88,151,759,25]: by Ari Chen 17 - - generic "Thing stats" [ref=e29] [box=920,157,272,19]: 18 - - generic [ref=e30] [box=0,0,0,0]: 19 - - term [ref=e31] [box=920,157,1,1]: Models 20 - - definition [ref=e32] [box=920,157,58,19]: 3 models 21 - - generic [ref=e33] [box=0,0,0,0]: 22 - - term [ref=e34] [box=920,157,1,1]: Parts 23 - - definition [ref=e35] [box=991,157,58,19]: 18 parts 24 - - generic [ref=e36] [box=0,0,0,0]: 25 - - term [ref=e37] [box=920,157,1,1]: Likes 26 - - definition [ref=e38] [box=1062,157,58,19]: 42 likes 27 - - generic [ref=e39] [box=0,0,0,0]: 28 - - term [ref=e40] [box=920,157,1,1]: Saves 29 - - definition [ref=e41] [box=1133,157,58,19]: 12 saves 30 - - region "Thing overview" [ref=e42] [box=88,191,1103,1310]: 31 - - generic "Asset-backed 3D model viewer" [ref=e44] [box=114,217,538,1258]: 32 - - generic [ref=e46] [box=114,217,538,1258]: 33 - - generic "Polymodel model viewer canvas" [ref=e47] [box=114,217,538,1021] 34 - - generic [ref=e48] [box=130,1253,506,205]: 35 - - heading "Viewer ready" [level=2] [ref=e49] [box=147,1270,472,37] 36 - - paragraph [ref=e50] [box=147,1307,472,25]: STL asset is loaded and ready for the canvas renderer. 37 - - generic [ref=e51] [box=147,1344,472,97]: 38 - - generic [ref=e52] [box=147,1344,228,45]: 39 - - term [ref=e53] [box=147,1344,228,17]: Model 40 - - definition [ref=e54] [box=147,1364,228,25]: Body F chest v4 41 - - generic [ref=e55] [box=392,1344,228,45]: 42 - - term [ref=e56] [box=392,1344,228,17]: Asset 43 - - definition [ref=e57] [box=392,1364,228,25]: /models/body_f_chest-v4.stl 44 - - generic [ref=e58] [box=147,1397,228,45]: 45 - - term [ref=e59] [box=147,1397,228,17]: Vertices 46 - - definition [ref=e60] [box=147,1417,228,25]: "41051" 47 - - generic [ref=e61] [box=392,1397,228,45]: 48 - - term [ref=e62] [box=392,1397,228,17]: Triangles 49 - - definition [ref=e63] [box=392,1417,228,25]: "82122" 50 - - generic [ref=e64] [box=678,216,489,1260]: 51 - - generic [ref=e65] [box=678,216,489,108]: 52 - - paragraph [ref=e66] [box=678,227,489,59]: A configurable electronics enclosure kit with three printable model variants and detailed part files. 53 - - generic "Thing tags and license" [ref=e67] [box=678,299,489,25]: 54 - - generic [ref=e68] [box=678,299,80,25]: CC-BY-4.0 55 - - generic [ref=e69] [box=766,299,87,25]: "#enclosure" 56 - - generic [ref=e70] [box=861,299,94,25]: "#parametric" 57 - - generic [ref=e71] [box=963,299,122,25]: "#print-in-place" 58 - - generic "Thing actions" [ref=e72] [box=678,1441,489,34]: 59 - - button "Download files" [ref=e73] [cursor=pointer] [box=678,1441,152,34] 60 - - button "Save disabled until sign in is available" [disabled] [ref=e74] [box=838,1441,61,34]: Save 61 - - region "Thing detail sections" [ref=e75] [box=88,1515,1103,1938]: 62 - - article [ref=e76] [box=88,1515,544,186]: 63 - - heading "Overview" [level=2] [ref=e78] [box=105,1532,118,28] 64 - - list [ref=e79] [box=105,1573,510,110]: 65 - - listitem [ref=e80] [box=125,1573,490,25]: Print the body and lid in PETG or PLA+ with 0.2 mm layers. 66 - - listitem [ref=e81] [box=125,1604,490,50]: Use the model selector to choose the standard, panel-mount, or ruggedized variant. 67 - - listitem [ref=e82] [box=125,1659,490,25]: Press brass inserts into the corner bosses before final assembly. 68 - - article [ref=e83] [box=648,1515,544,345]: 69 - - heading "Models" [level=2] [ref=e85] [box=665,1532,92,28] 70 - - list "Model variants" [ref=e86] [box=665,1573,510,270]: 71 - - listitem [ref=e87] [cursor=pointer] [box=665,1573,510,84]: 72 - - generic [ref=e88] [box=678,1586,484,18]: Snap-fit electronics enclosure 73 - - generic [ref=e89] [box=678,1608,484,15]: 6 parts · CC-BY-4.0 74 - - generic [ref=e90] [box=678,1627,484,17]: Main printable enclosure with lid, body, buttons, and mounting hardware. 75 - - listitem [ref=e91] [cursor=pointer] [box=665,1666,510,84]: 76 - - generic [ref=e92] [box=678,1679,484,18]: Panel-mount variant 77 - - generic [ref=e93] [box=678,1701,484,15]: 6 parts · CC-BY-4.0 78 - - generic [ref=e94] [box=678,1720,484,17]: Alternate front panel for flush mounting sensors and displays. 79 - - listitem [ref=e95] [cursor=pointer] [box=665,1759,510,84]: 80 - - generic [ref=e96] [box=678,1772,484,18]: Ruggedized outdoor lid 81 - - generic [ref=e97] [box=678,1794,484,15]: 6 parts · CC-BY-4.0 82 - - generic [ref=e98] [box=678,1813,484,17]: Weather-resistant lid and gasket parts for workshop deployments. 83 - - article [ref=e99] [box=88,1876,544,277]: 84 - - generic [ref=e100] [box=105,1893,510,243]: 85 - - generic [ref=e101] [box=105,1893,510,51]: 86 - - generic [ref=e102] [box=105,1893,510,18]: Current model 87 - - heading "Snap-fit electronics enclosure" [level=2] [ref=e103] [box=105,1916,510,28] 88 - - paragraph [ref=e104] [box=105,1970,510,50]: Main printable enclosure with lid, body, buttons, and mounting hardware. 89 - - list [ref=e105] [box=105,2046,510,55]: 90 - - listitem [ref=e106] [box=125,2046,490,25]: Review the ordered part list before printing. 91 - - listitem [ref=e107] [box=125,2077,490,25]: Print fit-critical clips slowly for best tolerances. 92 - - generic "Selected model tags" [ref=e108] [box=105,2112,510,25]: 93 - - generic [ref=e109] [box=105,2112,73,25]: "#variant" 94 - - generic [ref=e110] [box=187,2112,87,25]: "#enclosure" 95 - - article [ref=e111] [box=648,1876,544,1577]: 96 - - heading "Parts & files" [level=2] [ref=e113] [box=665,1893,148,28] 97 - - list "Ordered part files" [ref=e114] [box=665,1935,510,1501]: 98 - - listitem [ref=e115] [box=665,1935,510,240]: 99 - - generic "Part position" [ref=e116] [box=678,1948,32,32]: "1" 100 - - generic [ref=e117] [box=722,1948,440,214]: 101 - - heading "Main enclosure body" [level=3] [ref=e118] [box=722,1948,440,29] 102 - - generic "Part file metadata" [ref=e119] [box=722,1987,440,19]: 103 - - generic [ref=e120] [box=0,0,0,0] 104 - - generic [ref=e123] [box=0,0,0,0] 105 - - generic [ref=e126] [box=0,0,0,0] 106 - - paragraph [ref=e129] [box=722,2016,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 107 - - list "Print settings" [ref=e130] [box=722,2076,440,86]: 108 - - listitem [ref=e131] [box=742,2076,420,25]: 0.2 mm layer height 109 - - listitem [ref=e132] [box=742,2107,420,25]: 15% gyroid infill 110 - - listitem [ref=e133] [box=742,2137,420,25]: No supports required unless noted 111 - - listitem [ref=e134] [box=665,2187,510,240]: 112 - - generic "Part position" [ref=e135] [box=678,2200,32,32]: "2" 113 - - generic [ref=e136] [box=722,2200,440,214]: 114 - - heading "Snap-fit lid" [level=3] [ref=e137] [box=722,2200,440,29] 115 - - generic "Part file metadata" [ref=e138] [box=722,2239,440,19]: 116 - - generic [ref=e139] [box=0,0,0,0] 117 - - generic [ref=e142] [box=0,0,0,0] 118 - - generic [ref=e145] [box=0,0,0,0] 119 - - paragraph [ref=e148] [box=722,2269,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 120 - - list "Print settings" [ref=e149] [box=722,2329,440,86]: 121 - - listitem [ref=e150] [box=742,2329,420,25]: 0.2 mm layer height 122 - - listitem [ref=e151] [box=742,2359,420,25]: 15% gyroid infill 123 - - listitem [ref=e152] [box=742,2389,420,25]: No supports required unless noted 124 - - listitem [ref=e153] [box=665,2439,510,240]: 125 - - generic "Part position" [ref=e154] [box=678,2452,32,32]: "3" 126 - - generic [ref=e155] [box=722,2452,440,214]: 127 - - heading "Button cap set" [level=3] [ref=e156] [box=722,2452,440,29] 128 - - generic "Part file metadata" [ref=e157] [box=722,2491,440,19]: 129 - - generic [ref=e158] [box=0,0,0,0] 130 - - generic [ref=e161] [box=0,0,0,0] 131 - - generic [ref=e164] [box=0,0,0,0] 132 - - paragraph [ref=e167] [box=722,2521,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 133 - - list "Print settings" [ref=e168] [box=722,2581,440,86]: 134 - - listitem [ref=e169] [box=742,2581,420,25]: 0.2 mm layer height 135 - - listitem [ref=e170] [box=742,2611,420,25]: 15% gyroid infill 136 - - listitem [ref=e171] [box=742,2641,420,25]: No supports required unless noted 137 - - listitem [ref=e172] [box=665,2691,510,240]: 138 - - generic "Part position" [ref=e173] [box=678,2704,32,32]: "4" 139 - - generic [ref=e174] [box=722,2704,440,214]: 140 - - heading "USB-C bezel" [level=3] [ref=e175] [box=722,2704,440,29] 141 - - generic "Part file metadata" [ref=e176] [box=722,2744,440,19]: 142 - - generic [ref=e177] [box=0,0,0,0] 143 - - generic [ref=e180] [box=0,0,0,0] 144 - - generic [ref=e183] [box=0,0,0,0] 145 - - paragraph [ref=e186] [box=722,2773,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 146 - - list "Print settings" [ref=e187] [box=722,2833,440,86]: 147 - - listitem [ref=e188] [box=742,2833,420,25]: 0.2 mm layer height 148 - - listitem [ref=e189] [box=742,2863,420,25]: 15% gyroid infill 149 - - listitem [ref=e190] [box=742,2894,420,25]: No supports required unless noted 150 - - listitem [ref=e191] [box=665,2943,510,240]: 151 - - generic "Part position" [ref=e192] [box=678,2956,32,32]: "5" 152 - - generic [ref=e193] [box=722,2956,440,214]: 153 - - heading "PCB mounting tray" [level=3] [ref=e194] [box=722,2956,440,29] 154 - - generic "Part file metadata" [ref=e195] [box=722,2996,440,19]: 155 - - generic [ref=e196] [box=0,0,0,0] 156 - - generic [ref=e199] [box=0,0,0,0] 157 - - generic [ref=e202] [box=0,0,0,0] 158 - - paragraph [ref=e205] [box=722,3025,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 159 - - list "Print settings" [ref=e206] [box=722,3085,440,86]: 160 - - listitem [ref=e207] [box=742,3085,420,25]: 0.2 mm layer height 161 - - listitem [ref=e208] [box=742,3115,420,25]: 15% gyroid infill 162 - - listitem [ref=e209] [box=742,3146,420,25]: No supports required unless noted 163 - - listitem [ref=e210] [box=665,3196,510,240]: 164 - - generic "Part position" [ref=e211] [box=678,3209,32,32]: "6" 165 - - generic [ref=e212] [box=722,3209,440,214]: 166 - - heading "Corner screw boss" [level=3] [ref=e213] [box=722,3209,440,29] 167 - - generic "Part file metadata" [ref=e214] [box=722,3248,440,19]: 168 - - generic [ref=e215] [box=0,0,0,0] 169 - - generic [ref=e218] [box=0,0,0,0] 170 - - generic [ref=e221] [box=0,0,0,0] 171 - - paragraph [ref=e224] [box=722,3277,440,50]: Orient the visible face upward and verify first-layer adhesion around clips. 172 - - list "Print settings" [ref=e225] [box=722,3337,440,86]: 173 - - listitem [ref=e226] [box=742,3337,420,25]: 0.2 mm layer height 174 - - listitem [ref=e227] [box=742,3368,420,25]: 15% gyroid infill 175 - - listitem [ref=e228] [box=742,3398,420,25]: No supports required unless noted
+5 -1
src/shell.rs
··· 13 13 pub(crate) fn AppShell() -> Element { 14 14 let session = use_context::<Signal<SessionIdentity>>(); 15 15 let toasts = use_context::<Signal<ToastState>>(); 16 + let route = use_route::<Route>(); 17 + let show_browse_link = !matches!(route, Route::Browse {}); 16 18 17 19 rsx! { 18 20 div { class: "app-shell", ··· 25 27 "Polymodel" 26 28 } 27 29 nav { class: "app-nav", aria_label: "Primary", 28 - Link { class: "app-nav-link", active_class: "is-active", to: Route::Browse {}, "Browse" } 30 + if show_browse_link { 31 + Link { class: "app-nav-link", active_class: "is-active", to: Route::Browse {}, "Browse" } 32 + } 29 33 } 30 34 ShellSearch {} 31 35 Link { class: "button button-primary app-publish-cta", to: Route::Publish {}, "Publish" }