Skip to content

Commit 4016f0f

Browse files
feat: add short URL keys and tooltips for impl-level filters
- Use short URL keys for impl-level filters: dep, tech, pat, prep, style (consistent with spec-level filters like lib, plot, data) - Add FILTER_TOOLTIPS with descriptions for all 11 filter categories - Update Plausible analytics orderedKeys for tracking - Remove publication-ready from tagging docs example Issue #2434 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent ac5ee92 commit 4016f0f

File tree

7 files changed

+108
-91
lines changed

7 files changed

+108
-91
lines changed

api/routers/plots.py

Lines changed: 40 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -46,23 +46,23 @@ def _image_matches_groups(spec_id: str, library: str, groups: list[dict], spec_l
4646
if not any(v in spec_features for v in values):
4747
return False
4848
# Impl-level tag filters (issue #2434)
49-
elif category == "impl_dep":
49+
elif category == "dep":
5050
impl_deps = impl_tags.get("dependencies", [])
5151
if not any(v in impl_deps for v in values):
5252
return False
53-
elif category == "impl_tech":
53+
elif category == "tech":
5454
impl_techs = impl_tags.get("techniques", [])
5555
if not any(v in impl_techs for v in values):
5656
return False
57-
elif category == "impl_pat":
57+
elif category == "pat":
5858
impl_pats = impl_tags.get("patterns", [])
5959
if not any(v in impl_pats for v in values):
6060
return False
61-
elif category == "impl_prep":
61+
elif category == "prep":
6262
impl_preps = impl_tags.get("dataprep", [])
6363
if not any(v in impl_preps for v in values):
6464
return False
65-
elif category == "impl_style":
65+
elif category == "style":
6666
impl_styles = impl_tags.get("styling", [])
6767
if not any(v in impl_styles for v in values):
6868
return False
@@ -79,11 +79,11 @@ def _calculate_global_counts(all_specs: list) -> dict:
7979
"dom": {},
8080
"feat": {},
8181
# Impl-level tag counts (issue #2434)
82-
"impl_dep": {},
83-
"impl_tech": {},
84-
"impl_pat": {},
85-
"impl_prep": {},
86-
"impl_style": {},
82+
"dep": {},
83+
"tech": {},
84+
"pat": {},
85+
"prep": {},
86+
"style": {},
8787
}
8888

8989
for spec_obj in all_specs:
@@ -117,15 +117,15 @@ def _calculate_global_counts(all_specs: list) -> dict:
117117
# Count impl-level tags (issue #2434)
118118
impl_tags = impl.impl_tags or {}
119119
for dep in impl_tags.get("dependencies", []):
120-
global_counts["impl_dep"][dep] = global_counts["impl_dep"].get(dep, 0) + 1
120+
global_counts["dep"][dep] = global_counts["dep"].get(dep, 0) + 1
121121
for tech in impl_tags.get("techniques", []):
122-
global_counts["impl_tech"][tech] = global_counts["impl_tech"].get(tech, 0) + 1
122+
global_counts["tech"][tech] = global_counts["tech"].get(tech, 0) + 1
123123
for pat in impl_tags.get("patterns", []):
124-
global_counts["impl_pat"][pat] = global_counts["impl_pat"].get(pat, 0) + 1
124+
global_counts["pat"][pat] = global_counts["pat"].get(pat, 0) + 1
125125
for prep in impl_tags.get("dataprep", []):
126-
global_counts["impl_prep"][prep] = global_counts["impl_prep"].get(prep, 0) + 1
126+
global_counts["prep"][prep] = global_counts["prep"].get(prep, 0) + 1
127127
for style in impl_tags.get("styling", []):
128-
global_counts["impl_style"][style] = global_counts["impl_style"].get(style, 0) + 1
128+
global_counts["style"][style] = global_counts["style"].get(style, 0) + 1
129129

130130
# Sort counts
131131
for category in global_counts:
@@ -144,11 +144,11 @@ def _calculate_contextual_counts(filtered_images: list[dict], spec_id_to_tags: d
144144
"dom": {},
145145
"feat": {},
146146
# Impl-level tag counts (issue #2434)
147-
"impl_dep": {},
148-
"impl_tech": {},
149-
"impl_pat": {},
150-
"impl_prep": {},
151-
"impl_style": {},
147+
"dep": {},
148+
"tech": {},
149+
"pat": {},
150+
"prep": {},
151+
"style": {},
152152
}
153153

154154
for img in filtered_images:
@@ -178,15 +178,15 @@ def _calculate_contextual_counts(filtered_images: list[dict], spec_id_to_tags: d
178178

179179
# Count impl-level tags (issue #2434)
180180
for dep in impl_tags.get("dependencies", []):
181-
counts["impl_dep"][dep] = counts["impl_dep"].get(dep, 0) + 1
181+
counts["dep"][dep] = counts["dep"].get(dep, 0) + 1
182182
for tech in impl_tags.get("techniques", []):
183-
counts["impl_tech"][tech] = counts["impl_tech"].get(tech, 0) + 1
183+
counts["tech"][tech] = counts["tech"].get(tech, 0) + 1
184184
for pat in impl_tags.get("patterns", []):
185-
counts["impl_pat"][pat] = counts["impl_pat"].get(pat, 0) + 1
185+
counts["pat"][pat] = counts["pat"].get(pat, 0) + 1
186186
for prep in impl_tags.get("dataprep", []):
187-
counts["impl_prep"][prep] = counts["impl_prep"].get(prep, 0) + 1
187+
counts["prep"][prep] = counts["prep"].get(prep, 0) + 1
188188
for style in impl_tags.get("styling", []):
189-
counts["impl_style"][style] = counts["impl_style"].get(style, 0) + 1
189+
counts["style"][style] = counts["style"].get(style, 0) + 1
190190

191191
# Sort counts
192192
for category in counts:
@@ -239,19 +239,19 @@ def _calculate_or_counts(
239239
for v in spec_tags.get("features", []):
240240
group_counts[v] = group_counts.get(v, 0) + 1
241241
# Impl-level tag counts (issue #2434)
242-
elif category == "impl_dep":
242+
elif category == "dep":
243243
for v in impl_tags.get("dependencies", []):
244244
group_counts[v] = group_counts.get(v, 0) + 1
245-
elif category == "impl_tech":
245+
elif category == "tech":
246246
for v in impl_tags.get("techniques", []):
247247
group_counts[v] = group_counts.get(v, 0) + 1
248-
elif category == "impl_pat":
248+
elif category == "pat":
249249
for v in impl_tags.get("patterns", []):
250250
group_counts[v] = group_counts.get(v, 0) + 1
251-
elif category == "impl_prep":
251+
elif category == "prep":
252252
for v in impl_tags.get("dataprep", []):
253253
group_counts[v] = group_counts.get(v, 0) + 1
254-
elif category == "impl_style":
254+
elif category == "style":
255255
for v in impl_tags.get("styling", []):
256256
group_counts[v] = group_counts.get(v, 0) + 1
257257

@@ -284,11 +284,11 @@ def _parse_filter_groups(request: Request) -> list[dict]:
284284
"dom",
285285
"feat",
286286
# Impl-level categories (issue #2434)
287-
"impl_dep",
288-
"impl_tech",
289-
"impl_pat",
290-
"impl_prep",
291-
"impl_style",
287+
"dep",
288+
"tech",
289+
"pat",
290+
"prep",
291+
"style",
292292
)
293293
for key, value in query_params:
294294
if key in valid_categories and value:
@@ -421,11 +421,11 @@ async def get_filtered_plots(request: Request, db: AsyncSession = Depends(requir
421421
- data: Data type tag (numeric, categorical, etc.)
422422
- dom: Domain tag (statistics, finance, etc.)
423423
- feat: Features tag (basic, 3d, interactive, etc.)
424-
- impl_dep: Impl dependencies filter (scipy, sklearn, etc.)
425-
- impl_tech: Impl techniques filter (twin-axes, colorbar, etc.)
426-
- impl_pat: Impl patterns filter (data-generation, etc.)
427-
- impl_prep: Impl dataprep filter (kde, binning, etc.)
428-
- impl_style: Impl styling filter (publication-ready, etc.)
424+
- dep: Impl dependencies filter (scipy, sklearn, etc.)
425+
- tech: Impl techniques filter (twin-axes, colorbar, etc.)
426+
- pat: Impl patterns filter (data-generation, etc.)
427+
- prep: Impl dataprep filter (kde, binning, etc.)
428+
- style: Impl styling filter (minimal-chrome, etc.)
429429
430430
Returns:
431431
FilteredPlotsResponse with images, counts, and orCounts per group

api/schemas.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,11 @@ class FilterCountsResponse(BaseModel):
8282
dom: dict[str, int] = {}
8383
feat: dict[str, int] = {}
8484
# Impl-level filters (issue #2434)
85-
impl_dep: dict[str, int] = {}
86-
impl_tech: dict[str, int] = {}
87-
impl_pat: dict[str, int] = {}
88-
impl_prep: dict[str, int] = {}
89-
impl_style: dict[str, int] = {}
85+
dep: dict[str, int] = {}
86+
tech: dict[str, int] = {}
87+
pat: dict[str, int] = {}
88+
prep: dict[str, int] = {}
89+
style: dict[str, int] = {}
9090

9191

9292
class FilteredPlotsResponse(BaseModel):

app/src/components/FilterBar.tsx

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import useMediaQuery from '@mui/material/useMediaQuery';
1919
import { useTheme } from '@mui/material/styles';
2020

2121
import type { FilterCategory, ActiveFilters, FilterCounts } from '../types';
22-
import { FILTER_LABELS, FILTER_CATEGORIES } from '../types';
22+
import { FILTER_LABELS, FILTER_TOOLTIPS, FILTER_CATEGORIES } from '../types';
2323
import type { ImageSize } from '../constants';
2424
import { getAvailableValues, getAvailableValuesForGroup, getSearchResults } from '../utils';
2525

@@ -653,26 +653,32 @@ export function FilterBar({
653653
// Calculate actual index among visible items
654654
const visibleIdx = dropdownItems.findIndex((item) => item.type === 'category' && item.category === category);
655655
return (
656-
<MenuItem
656+
<Tooltip
657657
key={category}
658-
onClick={() => handleCategorySelect(category)}
659-
selected={visibleIdx === highlightedIndex}
660-
sx={{ fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace' }}
658+
title={FILTER_TOOLTIPS[category]}
659+
placement="right"
660+
arrow
661661
>
662-
<ListItemText
663-
primary={FILTER_LABELS[category]}
664-
secondary={`${availableVals.length} options`}
665-
primaryTypographyProps={{
666-
fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace',
667-
fontSize: '0.9rem',
668-
}}
669-
secondaryTypographyProps={{
670-
fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace',
671-
fontSize: '0.75rem',
672-
color: '#9ca3af',
673-
}}
674-
/>
675-
</MenuItem>
662+
<MenuItem
663+
onClick={() => handleCategorySelect(category)}
664+
selected={visibleIdx === highlightedIndex}
665+
sx={{ fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace' }}
666+
>
667+
<ListItemText
668+
primary={FILTER_LABELS[category]}
669+
secondary={`${availableVals.length} options`}
670+
primaryTypographyProps={{
671+
fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace',
672+
fontSize: '0.9rem',
673+
}}
674+
secondaryTypographyProps={{
675+
fontFamily: '"MonoLisa", "MonoLisa Fallback", monospace',
676+
fontSize: '0.75rem',
677+
color: '#9ca3af',
678+
}}
679+
/>
680+
</MenuItem>
681+
</Tooltip>
676682
);
677683
})
678684
: // Show search results or category values

app/src/hooks/useAnalytics.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ function buildPlausibleUrl(): string {
3131
"data",
3232
"dom",
3333
"feat",
34-
"impl_dep",
35-
"impl_tech",
36-
"impl_pat",
37-
"impl_prep",
38-
"impl_style",
34+
"dep",
35+
"tech",
36+
"pat",
37+
"prep",
38+
"style",
3939
];
4040

4141
for (const key of orderedKeys) {

app/src/types/index.ts

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ export type FilterCategory =
1919
| 'data'
2020
| 'dom'
2121
| 'feat'
22-
| 'impl_dep'
23-
| 'impl_tech'
24-
| 'impl_pat'
25-
| 'impl_prep'
26-
| 'impl_style';
22+
| 'dep'
23+
| 'tech'
24+
| 'pat'
25+
| 'prep'
26+
| 'style';
2727

2828
// Display labels for filter categories
2929
export const FILTER_LABELS: Record<FilterCategory, string> = {
@@ -35,11 +35,28 @@ export const FILTER_LABELS: Record<FilterCategory, string> = {
3535
dom: 'field',
3636
feat: 'extras',
3737
// Impl-level (issue #2434)
38-
impl_dep: 'uses',
39-
impl_tech: 'technique',
40-
impl_pat: 'pattern',
41-
impl_prep: 'dataprep',
42-
impl_style: 'style',
38+
dep: 'uses',
39+
tech: 'technique',
40+
pat: 'pattern',
41+
prep: 'dataprep',
42+
style: 'style',
43+
};
44+
45+
// Tooltip descriptions for filter categories
46+
export const FILTER_TOOLTIPS: Record<FilterCategory, string> = {
47+
// Spec-level: WHAT is visualized
48+
lib: 'python plotting library',
49+
spec: 'specific plot example by identifier',
50+
plot: 'type of visualization or chart',
51+
data: 'structure of the input data',
52+
dom: 'application domain or field',
53+
feat: 'special plot features and capabilities',
54+
// Impl-level: HOW the code implements it
55+
dep: 'external packages beyond the plotting library',
56+
tech: 'advanced visualization techniques in the code',
57+
pat: 'code structure and organization patterns',
58+
prep: 'statistical or mathematical data transformations',
59+
style: 'visual styling choices that differ from defaults',
4360
};
4461

4562
// All filter categories in display order
@@ -52,11 +69,11 @@ export const FILTER_CATEGORIES: FilterCategory[] = [
5269
'dom',
5370
'feat',
5471
// Impl-level (issue #2434)
55-
'impl_dep',
56-
'impl_tech',
57-
'impl_pat',
58-
'impl_prep',
59-
'impl_style',
72+
'dep',
73+
'tech',
74+
'pat',
75+
'prep',
76+
'style',
6077
];
6178

6279
// A single filter group (one chip) - values within are OR-linked

core/database/connection.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -114,12 +114,7 @@ def _create_direct_engine_sync():
114114
elif url.startswith("postgres://"):
115115
url = url.replace("postgres://", "postgresql+pg8000://")
116116

117-
engine_kwargs = {
118-
"echo": ENVIRONMENT == "development",
119-
"pool_size": 5,
120-
"max_overflow": 10,
121-
"pool_pre_ping": True,
122-
}
117+
engine_kwargs = {"echo": ENVIRONMENT == "development", "pool_size": 5, "max_overflow": 10, "pool_pre_ping": True}
123118

124119
engine = create_engine(url, **engine_kwargs)
125120

docs/reference/tagging-system.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,6 @@ impl_tags:
155155
- iteration-over-groups
156156
dataprep: []
157157
styling:
158-
- publication-ready
159158
- alpha-blending
160159
```
161160

0 commit comments

Comments
 (0)