alpha
Login
or
Join now
nonbinary.computer
/
polymodel
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
atproto Thingiverse but good
Star
0
Fork
0
Atom
Configure Feed
Issues
Pull Requests
Commits
Tags
Feed URL
Select the types of activity you want to include in your feed.
Overview
Issues
Pulls
Pipelines
de-slop the search page somewhat
author
Orual
date
3 weeks ago
(Jun 29, 2026, 7:06 PM -0400)
commit
da5cf4aa
da5cf4aa57151d570913336ab94dc94bf2ccf47d
parent
919dcb93
919dcb9363e6c7fbe759705e49e92ad8a7651744
change-id
rkktolmt
rkktolmtvmnzmuoowzsqoumnplltzrtz
+90
-73
6 changed files
Expand all
Collapse all
Unified
Split
assets
styling
home.css
placeholders.css
src
about.rs
search.rs
shell.rs
thing_card.rs
-1
assets/styling/home.css
View file
Reviewed
···
56
56
57
57
.hero-band-pitch {
58
58
margin: 0;
59
59
-
max-width: 60ch;
60
59
color: var(--color-text);
61
60
font-size: clamp(1rem, 2vw, 1.15rem);
62
61
line-height: 1.45;
+24
-8
assets/styling/placeholders.css
View file
Reviewed
···
9
9
max-width: 1180px;
10
10
margin: 0 auto;
11
11
padding: clamp(0.75rem, 2vw, 1.75rem) clamp(1rem, 3vw, 2.5rem);
12
12
-
display: flex;
13
13
-
flex-direction: column;
14
14
-
gap: 0.9rem;
12
12
+
display: grid;
13
13
+
grid-template-columns: minmax(10rem, 14rem) minmax(0, 1fr);
14
14
+
align-items: start;
15
15
+
gap: 1rem;
15
16
}
16
17
17
18
.search-intro {
18
19
display: flex;
19
20
flex-direction: column;
20
20
-
gap: 0.75rem;
21
21
-
padding: clamp(1.25rem, 3vw, 2rem);
21
21
+
gap: 0.5rem;
22
22
+
padding: 1rem;
22
23
}
23
24
24
25
.search-intro h1 {
25
26
margin: 0;
26
27
color: var(--color-emphasis);
27
28
letter-spacing: -0.03em;
29
29
+
font-size: 1.75rem;
30
30
+
line-height: 1;
28
31
}
29
32
30
30
-
.search-note {
33
33
+
.search-query {
31
34
margin: 0;
32
32
-
color: var(--color-muted);
33
33
-
font-size: 0.9rem;
35
35
+
color: var(--color-subtle);
36
36
+
font-family: var(--font-mono);
37
37
+
font-size: 0.85rem;
38
38
+
overflow-wrap: anywhere;
39
39
+
}
40
40
+
41
41
+
.search-content {
42
42
+
min-width: 0;
43
43
+
display: flex;
44
44
+
flex-direction: column;
45
45
+
gap: 1rem;
34
46
}
35
47
36
48
.search-results-grid {
···
59
71
}
60
72
61
73
@media (max-width: 820px) {
74
74
+
.search-page {
75
75
+
grid-template-columns: 1fr;
76
76
+
}
77
77
+
62
78
.search-results-grid {
63
79
grid-template-columns: 1fr;
64
80
}
-2
src/about.rs
View file
Reviewed
···
1
1
use dioxus::prelude::*;
2
2
3
3
-
use crate::Route;
4
4
-
5
3
#[component]
6
4
pub(crate) fn About() -> Element {
7
5
rsx! {
+52
-60
src/search.rs
View file
Reviewed
···
126
126
results.read().as_ref(),
127
127
);
128
128
129
129
-
let page_note = if route_query.is_empty() {
130
130
-
"Catalog search uses the local appview index. Start from the header search field."
131
131
-
} else {
132
132
-
"Catalog search uses the local appview index and returns projects from the Polymodel library."
133
133
-
};
134
134
-
135
129
rsx! {
136
130
main { class: "search-page",
137
131
section { class: "search-intro blueprint-panel", aria_label: "Search",
138
132
span { class: "status-pill status-muted", "Catalog search" }
139
139
-
h1 { "Search" }
140
140
-
if route_query.is_empty() {
141
141
-
p { class: "product-lede", "Search the Polymodel catalog by project name, tags, authors, and indexed description text." }
142
142
-
} else {
143
143
-
p { class: "product-lede", "Results for \"{route_query}\"" }
133
133
+
if !route_query.is_empty() {
134
134
+
p { class: "search-query", "\"{route_query}\"" }
144
135
}
145
145
-
p { class: "search-note", "{page_note}" }
146
136
}
147
137
148
148
-
match state {
149
149
-
SearchState::Idle => rsx! {
150
150
-
section { class: "state-card empty-state search-state-card", aria_label: "Search instructions",
151
151
-
span { class: "status-pill status-muted", "Ready" }
152
152
-
h2 { "Run a catalog search" }
153
153
-
p { "Use the header search field to look up printable projects in the indexed Polymodel catalog." }
154
154
-
Link { class: "button button-secondary", to: Route::Browse {}, "Browse instead" }
155
155
-
}
156
156
-
},
157
157
-
SearchState::Loading => rsx! {
158
158
-
section { class: "search-results-grid", aria_label: "Search results loading",
159
159
-
for index in 0..6 {
160
160
-
div { key: "search-skeleton-{index}", class: "state-card loading-state",
161
161
-
span { class: "status-pill", "Searching" }
162
162
-
div { class: "skeleton-line skeleton-wide" }
163
163
-
div { class: "skeleton-line" }
164
164
-
div { class: "skeleton-box" }
138
138
+
div { class: "search-content",
139
139
+
match state {
140
140
+
SearchState::Idle => rsx! {
141
141
+
section { class: "state-card empty-state search-state-card", aria_label: "Search instructions",
142
142
+
span { class: "status-pill status-muted", "Ready" }
143
143
+
h2 { "Search from the header" }
144
144
+
p { "Find indexed projects by name, tag, author, or description." }
145
145
+
Link { class: "button button-secondary", to: Route::Browse {}, "Browse instead" }
146
146
+
}
147
147
+
},
148
148
+
SearchState::Loading => rsx! {
149
149
+
section { class: "search-results-grid", aria_label: "Search results loading",
150
150
+
for index in 0..6 {
151
151
+
div { key: "search-skeleton-{index}", class: "state-card loading-state",
152
152
+
span { class: "status-pill", "Searching" }
153
153
+
div { class: "skeleton-line skeleton-wide" }
154
154
+
div { class: "skeleton-line" }
155
155
+
div { class: "skeleton-box" }
156
156
+
}
165
157
}
166
158
}
167
167
-
}
168
168
-
},
169
169
-
SearchState::Error(error) => rsx! {
170
170
-
section { class: "state-card error-state search-state-card", aria_label: "Search error",
171
171
-
h2 { "Search failed" }
172
172
-
p { "{error}" }
173
173
-
button { class: "button button-secondary", onclick: move |_| search.restart(), "Retry" }
174
174
-
}
175
175
-
},
176
176
-
SearchState::Empty => rsx! {
177
177
-
section { class: "state-card empty-state search-state-card", aria_label: "No search results",
178
178
-
span { class: "status-pill status-muted", "No matches" }
179
179
-
h2 { "No projects match \"{route_query}\"" }
180
180
-
p { "Try a broader term, a material, or a project category. The local index answered; it just came back empty." }
181
181
-
Link { class: "button button-secondary", to: Route::Browse {}, "Back to browse" }
182
182
-
}
183
183
-
},
184
184
-
SearchState::Populated(search_results) => rsx! {
185
185
-
section { class: "search-results-grid", aria_label: "Search results",
186
186
-
for thing in search_results.items.clone() {
187
187
-
SearchThingCard { key: "{thing.uri}", thing }
159
159
+
},
160
160
+
SearchState::Error(error) => rsx! {
161
161
+
section { class: "state-card error-state search-state-card", aria_label: "Search error",
162
162
+
h2 { "Search failed" }
163
163
+
p { "{error}" }
164
164
+
button { class: "button button-secondary", onclick: move |_| search.restart(), "Retry" }
188
165
}
189
189
-
}
190
190
-
{search_pagination(
191
191
-
client.clone(),
192
192
-
route_query.clone(),
193
193
-
results,
194
194
-
search_results.clone(),
195
195
-
search_results.cursor.clone(),
196
196
-
search_results.load_more.clone(),
197
197
-
)}
198
198
-
},
166
166
+
},
167
167
+
SearchState::Empty => rsx! {
168
168
+
section { class: "state-card empty-state search-state-card", aria_label: "No search results",
169
169
+
span { class: "status-pill status-muted", "No matches" }
170
170
+
h2 { "No projects match \"{route_query}\"" }
171
171
+
p { "Try a broader term, material, or category." }
172
172
+
Link { class: "button button-secondary", to: Route::Browse {}, "Back to browse" }
173
173
+
}
174
174
+
},
175
175
+
SearchState::Populated(search_results) => rsx! {
176
176
+
section { class: "search-results-grid", aria_label: "Search results",
177
177
+
for thing in search_results.items.clone() {
178
178
+
SearchThingCard { key: "{thing.uri}", thing }
179
179
+
}
180
180
+
}
181
181
+
{search_pagination(
182
182
+
client.clone(),
183
183
+
route_query.clone(),
184
184
+
results,
185
185
+
search_results.clone(),
186
186
+
search_results.cursor.clone(),
187
187
+
search_results.load_more.clone(),
188
188
+
)}
189
189
+
},
190
190
+
}
199
191
}
200
192
}
201
193
}
+14
-1
src/shell.rs
View file
Reviewed
···
59
59
/// `SessionControl`, which issues a real GET to `/oauth/start`).
60
60
#[component]
61
61
fn ShellSearch() -> Element {
62
62
-
let mut query = use_signal(String::new);
62
62
+
let route = use_route::<Route>();
63
63
+
let route_query = match route {
64
64
+
Route::Search { q } => Some(q),
65
65
+
_ => None,
66
66
+
};
67
67
+
let mut query = use_signal(|| route_query.clone().unwrap_or_default());
68
68
+
let mut synced_route_query = use_signal(|| route_query.clone());
69
69
+
70
70
+
if *synced_route_query.read() != route_query {
71
71
+
synced_route_query.set(route_query.clone());
72
72
+
if let Some(route_query) = route_query {
73
73
+
query.set(route_query);
74
74
+
}
75
75
+
}
63
76
64
77
rsx! {
65
78
div { class: "app-search", role: "search",
-1
src/thing_card.rs
View file
Reviewed
···
143
143
img { src: "{media.url}", alt: "{media.alt}" }
144
144
} else {
145
145
div { class: "thing-card-placeholder blueprint-media", role: "img", aria_label: "Blueprint placeholder for missing preview media",
146
146
-
span { class: "status-pill status-muted", "No preview" }
147
146
span { class: "blueprint-axis blueprint-axis-x", "X" }
148
147
span { class: "blueprint-axis blueprint-axis-y", "Y" }
149
148
span { class: "blueprint-dimension", "preview pending" }