Skip to content

Update admin page UI designs#9357

Open
hotzenklotz wants to merge 26 commits intomasterfrom
update-admin-page-design
Open

Update admin page UI designs#9357
hotzenklotz wants to merge 26 commits intomasterfrom
update-admin-page-design

Conversation

@hotzenklotz
Copy link
Copy Markdown
Member

@hotzenklotz hotzenklotz commented Mar 4, 2026

This PR updates and unifies the admin page UI design.

  • Wrapped all admin pages in a <AdminListPage> component that layouts title, actions, search and the child content
  • Added search to some components that did not already have it
  • Added descriptions and link to the WK docs for all these pages
  • Updated the docs to include Script, Time Tracking and Project Progress view

URL of deployed dev instance (used for testing):

  • https://___.webknossos.xyz

Steps to test:

  • Click through the Admin, Analytics and Statistics and do a short visual check

Issues:

  • fixes #

(Please delete unneeded items, merge only when none are left open)

  • Added changelog entry (create a $PR_NUMBER.md file in unreleased_changes or use ./tools/create-changelog-entry.py)
  • Added migration guide entry if applicable (edit the same file as for the changelog)
  • Updated documentation if applicable
  • Adapted wk-libs python client if relevant API parts change
  • Removed dev-only changes like prints and application.conf edits
  • Considered common edge cases
  • Needs datastore update after deployment

@hotzenklotz hotzenklotz self-assigned this Mar 4, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Introduces a new AdminPage component and AnnotationServicesAd; migrates many admin views to use AdminPage; adds Scripts docs and expands Projects/Time Tracking docs; small UI tweaks (modal label, team selection API) and minor typing/layout adjustments.

Changes

Cohort / File(s) Summary
Documentation: Tasks & Projects
docs/tasks_projects/index.md, docs/tasks_projects/projects.md, docs/tasks_projects/scripts.md
Added a Scripts documentation page, updated index to reference multiple pages, and expanded Project Progress and Time Tracking documentation with UI, filters, and data columns.
Core Admin components
frontend/javascripts/admin/admin_page.tsx, frontend/javascripts/admin/ads/annotation_services_alert.tsx
Added AdminPage layout component (title, descriptionURI, docs link, search, actions, filters, alerts, responsive table theming) and a presentational AnnotationServicesAd.
Admin views — General migration
frontend/javascripts/admin/.../*.tsx
Many admin views (jobs, projects, scripts, statistics, tasks, tasktypes, teams, users, voxelytics, workflow, ai_model_list_view, etc.) were refactored to use AdminPage for header/search/actions/filters; preserved core logic and data fetching while consolidating layout.
Scripts UI & Viewer
frontend/javascripts/admin/scripts/script_list_view.tsx, frontend/javascripts/admin/scripts/script_create_view.tsx, frontend/javascripts/viewer/view/action_bar/user_scripts_modal_view.tsx
UI moved into AdminPage; script create/list unchanged functionally; viewer modal OK label changed from "Add" to "Run Script" (no behavior change).
Team selection / Dashboard changes
frontend/javascripts/dashboard/dataset/team_selection_component.tsx, frontend/javascripts/admin/statistic/team_selection_form.tsx
TeamSelectionComponent gains prefix?: ReactNode and updated showSearch config; team_selection_form.tsx file deleted (default export removed).
Project & Time tracking reports
frontend/javascripts/admin/statistic/project_progress_report_view.tsx, .../time_tracking_overview.tsx, .../available_tasks_report_view.tsx, .../time_tracking_detail_view.tsx, .../project_and_annotation_type_dropdown.tsx
Moved headers/filters/actions into AdminPage; restructured Project Progress table (Instance ColumnGroup), adjusted table alignments, summary row alignment, and minor typing simplifications.
Task/TaskType/Team/User/Voxelytics views
frontend/javascripts/admin/task/..., .../tasktype/..., .../team/..., .../user/..., .../voxelytics/...
Replaced bespoke header/search scaffolding with AdminPage; added AnnotationServicesAd to tasks view; retained form/table logic and validations.
Small UI/formatting tweaks
frontend/javascripts/components/formatted_id.tsx, frontend/javascripts/admin/statistic/project_and_annotation_type_dropdown.tsx
Adjusted formatted ID layout to use Typography/Space; updated dropdown placeholder, minWidth, popupMatchSelectWidth and maxTagCount.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • philippotto
  • knollengewaechs

Poem

🐰
I hopped through headers, tidy and spry,
Pages and scripts all stacked up high,
AdminPage stitched actions, search, and guides,
Filters aligned where the carrot-hidden pride hides.
A tiny rabbit cheers — UI freshly spry!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 7.14% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Update admin page UI designs' accurately reflects the main change: consolidating admin pages with a unified UI component and layout improvements.
Description check ✅ Passed The description is related to the changeset, explaining the wrapper component (AdminListPage), search additions, and documentation updates that align with the file changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch update-admin-page-design

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
frontend/javascripts/admin/voxelytics/workflow_list_view.tsx (1)

174-176: Consider adding a documentation link.

Other admin pages in this PR include a descriptionURI prop linking to relevant documentation. If documentation exists for Voxelytics workflows, consider adding it here for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/voxelytics/workflow_list_view.tsx` around lines
174 - 176, Add a descriptionURI prop to the AdminPage component instantiation in
workflow_list_view.tsx (the AdminPage with title "Voxelytics Workflows") so it
matches other admin pages; set descriptionURI to the relevant docs URL for
Voxelytics workflows (or to the internal docs route if available). Ensure the
prop name is descriptionURI and place it alongside title and description in the
AdminPage props so it follows the same pattern as other admin pages.
frontend/javascripts/viewer/view/action_bar/user_scripts_modal_view.tsx (1)

87-88: Consider aligning the modal title with the updated button text.

The okText was changed to "Run Script" which accurately reflects the action (executing via eval), but the modal title still says "Add User Script". Consider updating the title to something like "Run User Script" for consistency.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/viewer/view/action_bar/user_scripts_modal_view.tsx`
around lines 87 - 88, Update the modal title to match the action by changing the
Modal's title prop from "Add User Script" to "Run User Script" (or similar) so
it aligns with okText="Run Script"; locate the Modal in
user_scripts_modal_view.tsx (the component rendering the modal with title and
okText props) and update the title string accordingly, ensuring any related
accessibility labels or test IDs referencing the old title are updated to keep
tests and a11y consistent.
docs/tasks_projects/scripts.md (1)

3-7: Add an explicit trust/safety warning for script execution.

Since scripts execute arbitrary JavaScript, this page should clearly instruct admins to use only trusted sources and review changes before assignment.

Suggested addition
 Scripts let you run custom JavaScript logic automatically when annotators open assigned tasks.
 They are useful for project-specific keyboard shortcuts, UI helpers, and task setup automation.
+
+> [!WARNING]
+> Scripts execute arbitrary JavaScript in the annotator's browser context.
+> Only use trusted script sources/owners, and review script updates before assigning them to tasks.

Also applies to: 68-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/tasks_projects/scripts.md` around lines 3 - 7, Add a clear trust/safety
warning near the top of the "Scripts let you run custom JavaScript logic…"
section (the opening paragraph that links to the WEBKNOSSOS Frontend API
documentation) instructing admins to run only scripts from trusted sources, to
review code changes before assigning scripts to annotators, and to consider
restricting script usage to trusted users or environments; also replicate the
same warning where the scripts feature is described later in the document (the
other scripts description block referenced in the review) so both places contain
the explicit safety guidance.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/tasks_projects/projects.md`:
- Line 59: Update the bullet text that currently reads "Expandable rows for
detailed per-user time tracking entries." to hyphenate the compound modifier by
changing "time tracking entries" to "time-tracking entries" so the phrase
becomes "Expandable rows for detailed per-user time-tracking entries." Locate
and edit the exact line containing that sentence in the projects.md content.

In `@frontend/javascripts/admin/scripts/script_list_view.tsx`:
- Around line 163-171: Replace the legacy boolean prop on the Ant Design Space
component by changing the Space usage in script_list_view (the element wrapping
Link and LinkButton around EditOutlined/DeleteOutlined) from <Space vertical
...> to use the modern prop orientation="vertical"; keep other props and
children (Link to={`/scripts/${script.id}/edit`}, LinkButton
onClick={partial(deleteScript, script)} ) unchanged to ensure v6 compatibility.

In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx`:
- Around line 282-287: In the Select JSX in task_type_create_view.tsx, remove
the standalone prop optionFilterProp="label" and instead pass it inside the
showSearch object (e.g., change to showSearch={{ optionFilterProp: 'label' }}
and keep other showSearch settings as needed) so the Select conforms to Ant
Design v6; update the Select declaration (the component using mode="multiple",
allowClear, placeholder, etc.) to use the new showSearch format and delete the
separate optionFilterProp prop.

---

Nitpick comments:
In `@docs/tasks_projects/scripts.md`:
- Around line 3-7: Add a clear trust/safety warning near the top of the "Scripts
let you run custom JavaScript logic…" section (the opening paragraph that links
to the WEBKNOSSOS Frontend API documentation) instructing admins to run only
scripts from trusted sources, to review code changes before assigning scripts to
annotators, and to consider restricting script usage to trusted users or
environments; also replicate the same warning where the scripts feature is
described later in the document (the other scripts description block referenced
in the review) so both places contain the explicit safety guidance.

In `@frontend/javascripts/admin/voxelytics/workflow_list_view.tsx`:
- Around line 174-176: Add a descriptionURI prop to the AdminPage component
instantiation in workflow_list_view.tsx (the AdminPage with title "Voxelytics
Workflows") so it matches other admin pages; set descriptionURI to the relevant
docs URL for Voxelytics workflows (or to the internal docs route if available).
Ensure the prop name is descriptionURI and place it alongside title and
description in the AdminPage props so it follows the same pattern as other admin
pages.

In `@frontend/javascripts/viewer/view/action_bar/user_scripts_modal_view.tsx`:
- Around line 87-88: Update the modal title to match the action by changing the
Modal's title prop from "Add User Script" to "Run User Script" (or similar) so
it aligns with okText="Run Script"; locate the Modal in
user_scripts_modal_view.tsx (the component rendering the modal with title and
okText props) and update the title string accordingly, ensuring any related
accessibility labels or test IDs referencing the old title are updated to keep
tests and a11y consistent.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b6834540-69d0-4dbb-a59d-1045384392f9

📥 Commits

Reviewing files that changed from the base of the PR and between 5175fc1 and ad759fc.

📒 Files selected for processing (21)
  • docs/tasks_projects/index.md
  • docs/tasks_projects/projects.md
  • docs/tasks_projects/scripts.md
  • frontend/javascripts/admin/admin_page.tsx
  • frontend/javascripts/admin/ads/annotation_services_alert.tsx
  • frontend/javascripts/admin/job/job_list_view.tsx
  • frontend/javascripts/admin/project/project_create_view.tsx
  • frontend/javascripts/admin/project/project_list_view.tsx
  • frontend/javascripts/admin/scripts/script_create_view.tsx
  • frontend/javascripts/admin/scripts/script_list_view.tsx
  • frontend/javascripts/admin/statistic/available_tasks_report_view.tsx
  • frontend/javascripts/admin/statistic/project_progress_report_view.tsx
  • frontend/javascripts/admin/statistic/time_tracking_overview.tsx
  • frontend/javascripts/admin/task/task_list_view.tsx
  • frontend/javascripts/admin/tasktype/task_type_create_view.tsx
  • frontend/javascripts/admin/tasktype/task_type_list_view.tsx
  • frontend/javascripts/admin/team/team_list_view.tsx
  • frontend/javascripts/admin/user/user_list_view.tsx
  • frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx
  • frontend/javascripts/admin/voxelytics/workflow_list_view.tsx
  • frontend/javascripts/viewer/view/action_bar/user_scripts_modal_view.tsx

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
frontend/javascripts/admin/tasktype/task_type_create_view.tsx (2)

82-85: ⚠️ Potential issue | 🟠 Major

Ensure team loading state is cleared on fetch failures.

If getEditableTeams() throws, isFetchingData never flips to false, leaving the Team <Select> stuck in loading state.

Suggested fix
   const teams = useFetch(
     async () => {
-      const teams = await getEditableTeams();
-      setIsFetchingData(false);
-      return teams;
+      try {
+        return await getEditableTeams();
+      } finally {
+        setIsFetchingData(false);
+      }
     },
     [],
     [],
   );
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx` around lines
82 - 85, The async loader that calls getEditableTeams() must always clear the
fetching flag; wrap the await in a try/finally so setIsFetchingData(false) runs
whether getEditableTeams() resolves or throws. Specifically, update the async
arrow function that calls getEditableTeams() to use try { const teams = await
getEditableTeams(); return teams } finally { setIsFetchingData(false) }, and
decide whether to rethrow the error or return an empty array depending on
existing error handling.

56-70: ⚠️ Potential issue | 🟡 Minor

Fix user-facing validation typos in magnification errors.

The messages use “then” where “than” is intended (Line 61, Line 69).

Suggested fix
   return Promise.reject(
-    new Error("The minimum magnification needs to be smaller then the maximum mag."),
+    new Error("The minimum magnification needs to be smaller than the maximum mag."),
   );
 }
 function isMaximumMagnificationSmallerThenMinRule(value: number | undefined, minMag: number) {
   if (value && value >= minMag) {
     return Promise.resolve();
   }
   return Promise.reject(
-    new Error("The maximum magnification needs to be larger then the minimum mag."),
+    new Error("The maximum magnification needs to be larger than the minimum mag."),
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx` around lines
56 - 70, The error messages in isMinimumMagnifactionLargerThenMaxRule and
isMaximumMagnificationSmallerThenMinRule use the incorrect word "then" instead
of "than"; update the Promise.reject error strings to use "than" (e.g., "The
minimum magnification needs to be smaller than the maximum mag." and "The
maximum magnification needs to be larger than the minimum mag.") so the
user-facing validation messages are grammatically correct.
🧹 Nitpick comments (2)
docs/tasks_projects/projects.md (2)

30-31: Standardize UI action formatting for button labels.

Line 30 uses “Click Search” while other UI labels are consistently code-formatted. Consider Click \Search`.` for consistency and scanability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/tasks_projects/projects.md` around lines 30 - 31, Standardize the UI
action text by replacing the plain "Click Search" phrase with a code-formatted
UI label `Click `Search`` so it matches other button labels; locate the
occurrence of the string "Click Search" in projects.md (around the step list)
and change it to use inline code formatting for the button name (`Search`), and
scan the surrounding list items to ensure other actions follow the same `Click
`ButtonName`` pattern for consistency.

51-53: Clarify the filter wording to avoid ambiguity.

“Include only tasks from projects and/or all annotations” is hard to parse. Consider naming the exact filter controls/choices as shown in the UI so users can map steps directly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/tasks_projects/projects.md` around lines 51 - 53, The phrasing "Include
only tasks from projects and/or all annotations" is ambiguous; update the docs
to use the exact UI control labels so users can map directly — replace that line
with explicit options such as "Project filter: [All projects | Specific
project]" and "Annotations filter: [All annotations | Only annotations from
selected projects]" (or whatever the UI shows) and ensure the list items match
the UI control names shown in the app.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx`:
- Around line 307-333: The "Any" option uses an empty string which can violate
the API type for settings.preferredMode (APIAllowedMode | undefined); in the
Select under FormItem name=["settings","preferredMode"] replace the empty-string
value for the "Any" option with undefined (or remove the value so it maps to
undefined) so the form submits undefined instead of "", and ensure allowClear
behaviour also results in undefined; alternatively, add a submit-time transform
that maps "" to undefined before sending to the API.

---

Outside diff comments:
In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx`:
- Around line 82-85: The async loader that calls getEditableTeams() must always
clear the fetching flag; wrap the await in a try/finally so
setIsFetchingData(false) runs whether getEditableTeams() resolves or throws.
Specifically, update the async arrow function that calls getEditableTeams() to
use try { const teams = await getEditableTeams(); return teams } finally {
setIsFetchingData(false) }, and decide whether to rethrow the error or return an
empty array depending on existing error handling.
- Around line 56-70: The error messages in
isMinimumMagnifactionLargerThenMaxRule and
isMaximumMagnificationSmallerThenMinRule use the incorrect word "then" instead
of "than"; update the Promise.reject error strings to use "than" (e.g., "The
minimum magnification needs to be smaller than the maximum mag." and "The
maximum magnification needs to be larger than the minimum mag.") so the
user-facing validation messages are grammatically correct.

---

Nitpick comments:
In `@docs/tasks_projects/projects.md`:
- Around line 30-31: Standardize the UI action text by replacing the plain
"Click Search" phrase with a code-formatted UI label `Click `Search`` so it
matches other button labels; locate the occurrence of the string "Click Search"
in projects.md (around the step list) and change it to use inline code
formatting for the button name (`Search`), and scan the surrounding list items
to ensure other actions follow the same `Click `ButtonName`` pattern for
consistency.
- Around line 51-53: The phrasing "Include only tasks from projects and/or all
annotations" is ambiguous; update the docs to use the exact UI control labels so
users can map directly — replace that line with explicit options such as
"Project filter: [All projects | Specific project]" and "Annotations filter:
[All annotations | Only annotations from selected projects]" (or whatever the UI
shows) and ensure the list items match the UI control names shown in the app.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 435a2573-6b5e-465b-ad26-8a156f4b8701

📥 Commits

Reviewing files that changed from the base of the PR and between ad759fc and 519fd05.

📒 Files selected for processing (3)
  • docs/tasks_projects/projects.md
  • frontend/javascripts/admin/scripts/script_list_view.tsx
  • frontend/javascripts/admin/tasktype/task_type_create_view.tsx

Comment on lines +307 to +333
<FormItem name={["settings", "preferredMode"]} label="Preferred Mode" hasFeedback>
<Select
allowClear
showSearch={{ optionFilterProp: "label" }}
style={{
width: "100%",
}}
options={[
{
required: true,
value: "",
label: "Any",
},
]}
>
<Select
allowClear
showSearch={{ optionFilterProp: "label" }}
placeholder="Select a Team"
style={{
width: "100%",
}}
loading={isFetchingData}
options={teams.map((team: APITeam) => ({
value: team.id,
label: `${team.name}`,
}))}
/>
</FormItem>

<FormItem
name="description"
label={
<span>
Description (
<a href="https://markdown-it.github.io/" target="_blank" rel="noopener noreferrer">
Markdown enabled
</a>
)
</span>
}
hasFeedback
rules={[
{
required: true,
value: "orthogonal",
label: "Orthogonal",
},
{
value: "oblique",
label: "Oblique",
},
]}
>
<TextArea rows={10} />
</FormItem>

<FormItem name="tracingType" label="Annotation Type">
<RadioGroup>
<Radio value="skeleton" disabled={isEditingMode}>
Skeleton
</Radio>
<Radio value="volume" disabled={isEditingMode}>
Volume
</Radio>
<Radio value="hybrid" disabled={isEditingMode}>
Skeleton and Volume
</Radio>
</RadioGroup>
</FormItem>

<FormItem
name={["settings", "allowedModes"]}
label="Allowed Modes"
hasFeedback
rules={[
{
required: true,
value: "flight",
label: "Flight",
},
]}
>
<Select
mode="multiple"
allowClear
placeholder="Select all Allowed Modes"
optionFilterProp="label"
style={{
width: "100%",
}}
options={[
{
value: "orthogonal",
label: "Orthogonal",
},
{
value: "oblique",
label: "Oblique",
},
{
value: "flight",
label: "Flight",
},
]}
/>
</FormItem>

<FormItem name={["settings", "preferredMode"]} label="Preferred Mode" hasFeedback>
<Select
allowClear
showSearch={{ optionFilterProp: "label" }}
style={{
width: "100%",
}}
options={[
{
value: "",
label: "Any",
},
{
value: "orthogonal",
label: "Orthogonal",
},
{
value: "oblique",
label: "Oblique",
},
{
value: "flight",
label: "Flight",
},
]}
/>
</FormItem>

<FormItem
noStyle
shouldUpdate={(prevValues, curValues) =>
prevValues.tracingType !== curValues.tracingType
}
>
{({ getFieldValue }) => (
<div>
{/* Skeleton-specific */}
<div
style={{
// These form items are always emitted here and only their visibility
// is changed, since the values are always needed to create/edit
// a task type (its schema requires it even though the fields are
// irrelevant for volume-only tasks).
display:
getFieldValue(["tracingType"]) === TracingTypeEnum.volume ? "none" : "block",
}}
>
<FormItem
name={["settings", "somaClickingAllowed"]}
label="Settings"
valuePropName="checked"
>
<Checkbox>Allow Single-node-tree mode (&quot;Soma clicking&quot;)</Checkbox>
</FormItem>

<FormItem name={["settings", "branchPointsAllowed"]} valuePropName="checked">
<Checkbox>Allow Branchpoints</Checkbox>
</FormItem>
<FormItem name={["settings", "mergerMode"]} valuePropName="checked">
<Checkbox>Merger Mode</Checkbox>
</FormItem>
</div>

{/* Volume-specific */}
<div
style={{
// These form items are always emitted here and only their visibility
// is changed, since the values are always needed to create/edit
// a task type (its schema requires it even though the fields are
// irrelevant for skeleton-only tasks).
display:
getFieldValue(["tracingType"]) === TracingTypeEnum.skeleton
? "none"
: "block",
}}
/>
</FormItem>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

preferredMode currently allows an invalid empty-string value.

Line 316 sets value: "" for “Any”, but settings.preferredMode is typed as APIAllowedMode | undefined. Submitting "" can violate the API contract and cause backend validation issues.

Suggested fix
         <FormItem name={["settings", "preferredMode"]} label="Preferred Mode" hasFeedback>
           <Select
             allowClear
+            placeholder="Any"
             showSearch={{ optionFilterProp: "label" }}
             style={{
               width: "100%",
             }}
             options={[
-              {
-                value: "",
-                label: "Any",
-              },
               {
                 value: "orthogonal",
                 label: "Orthogonal",
               },
               {
                 value: "oblique",
                 label: "Oblique",
               },
               {
                 value: "flight",
                 label: "Flight",
               },
             ]}
           />
         </FormItem>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/tasktype/task_type_create_view.tsx` around lines
307 - 333, The "Any" option uses an empty string which can violate the API type
for settings.preferredMode (APIAllowedMode | undefined); in the Select under
FormItem name=["settings","preferredMode"] replace the empty-string value for
the "Any" option with undefined (or remove the value so it maps to undefined) so
the form submits undefined instead of "", and ensure allowClear behaviour also
results in undefined; alternatively, add a submit-time transform that maps "" to
undefined before sending to the API.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/javascripts/admin/statistic/time_tracking_overview.tsx (1)

241-283: Make the filter bar wrap to avoid overflow on narrower widths.

Lines 241–283 place several wide controls in a non-wrapping Space, which can push content horizontally. Adding wrap keeps the new AdminPage layout responsive.

♻️ Suggested diff
-      filters={
-        <Space>
+      filters={
+        <Space wrap>
           <ProjectAndAnnotationTypeDropdown
             setSelectedProjectIds={setSelectedProjectIds}
             selectedProjectIds={selectedProjectIds}
             setSelectedAnnotationType={setSelectedTypes}
             selectedAnnotationType={selectedTypes}
             selectedAnnotationState={selectedState}
             setSelectedAnnotationState={setSelectedState}
           />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/time_tracking_overview.tsx` around lines
241 - 283, The toolbar uses an Ant Design <Space> that prevents wrapping and
causes horizontal overflow; update the container around
ProjectAndAnnotationTypeDropdown, the Select, and the RangePicker to allow
wrapping (e.g., enable the Space wrap behavior or switch to a flex container
with flexWrap:'wrap' and appropriate alignment/spacing) so the controls reflow
on narrow widths—adjust the <Space> (wrapping) or replace it with a responsive
flex/Row wrapper and keep the existing props/state handlers
(setSelectedProjectIds, selectedProjectIds, setSelectedTypes, selectedTypes,
selectedState, setSelectedState, setSelectedTeams, startDate, endDate,
setStartDate, setEndeDate) intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/javascripts/admin/statistic/project_progress_report_view.tsx`:
- Around line 175-207: Rows where item.totalInstances === 0 are treated as
finished (showing "100%") and passed as all-zero values into the
StackedBarChart, which can produce invalid rendering; update the render logic in
the Column renderer for finishedInstances to first check item.totalInstances ===
0 and render an explicit "0%" or a neutral Badge (and render StackedBarChart
with a guard/early-return or a special empty-state) instead of computing
Math.floor((100 * finishedInstances) / item.totalInstances) or passing a,b,c
when total is zero. Locate the Column with title Badge/finished and the render
function that returns colSpan/children (uses finishedInstances, activeInstances,
pendingInstances and StackedBarChart) and add the zero-total branch there.

---

Nitpick comments:
In `@frontend/javascripts/admin/statistic/time_tracking_overview.tsx`:
- Around line 241-283: The toolbar uses an Ant Design <Space> that prevents
wrapping and causes horizontal overflow; update the container around
ProjectAndAnnotationTypeDropdown, the Select, and the RangePicker to allow
wrapping (e.g., enable the Space wrap behavior or switch to a flex container
with flexWrap:'wrap' and appropriate alignment/spacing) so the controls reflow
on narrow widths—adjust the <Space> (wrapping) or replace it with a responsive
flex/Row wrapper and keep the existing props/state handlers
(setSelectedProjectIds, selectedProjectIds, setSelectedTypes, selectedTypes,
selectedState, setSelectedState, setSelectedTeams, startDate, endDate,
setStartDate, setEndeDate) intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2929bbce-7541-48ed-8335-f9789b1fefab

📥 Commits

Reviewing files that changed from the base of the PR and between 519fd05 and 9908212.

📒 Files selected for processing (6)
  • frontend/javascripts/admin/statistic/available_tasks_report_view.tsx
  • frontend/javascripts/admin/statistic/project_and_annotation_type_dropdown.tsx
  • frontend/javascripts/admin/statistic/project_progress_report_view.tsx
  • frontend/javascripts/admin/statistic/team_selection_form.tsx
  • frontend/javascripts/admin/statistic/time_tracking_overview.tsx
  • frontend/javascripts/dashboard/dataset/team_selection_component.tsx
💤 Files with no reviewable changes (1)
  • frontend/javascripts/admin/statistic/team_selection_form.tsx

Comment on lines +175 to +207
finishedInstances === item.totalInstances ? (
<Badge
count="100%"
style={{
backgroundColor: colors.finished,
}}
/>
) : (
<span>{Math.floor((100 * finishedInstances) / item.totalInstances)} %</span>
)
}
/>
<Column
title={
<Badge
count="Finished"
style={{
background: colors.finished,
}}
/>
}
dataIndex="finishedInstances"
sorter={compareBy<APIProjectProgressReport>((project) => project.finishedInstances)}
render={(_text, item: APIProjectProgressReport) => ({
props: {
colSpan: 3,
},
children: (
<StackedBarChart
a={item.finishedInstances}
b={item.activeInstances}
c={item.pendingInstances}
/>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle zero-instance projects explicitly in progress/chart rendering.

At Lines 175–207, rows with totalInstances === 0 can display misleading progress (100%) and may feed all-zero values into StackedBarChart, which can render invalid widths. Add an explicit zero-total path before percentage/chart rendering.

🐛 Suggested diff
               <Column
                 title="Progress"
                 key="progress"
                 dataIndex="finishedInstances"
                 width={100}
                 sorter={compareBy<APIProjectProgressReport>(
                   ({ finishedInstances, totalInstances }) => finishedInstances / totalInstances,
                 )}
-                render={(finishedInstances, item) =>
-                  finishedInstances === item.totalInstances ? (
+                render={(finishedInstances, item) => {
+                  if (item.totalInstances === 0) {
+                    return <span>0 %</span>;
+                  }
+                  return finishedInstances === item.totalInstances ? (
                     <Badge
                       count="100%"
                       style={{
                         backgroundColor: colors.finished,
                       }}
                     />
                   ) : (
                     <span>{Math.floor((100 * finishedInstances) / item.totalInstances)} %</span>
-                  )
-                }
+                  );
+                }}
               />
@@
                 dataIndex="finishedInstances"
                 sorter={compareBy<APIProjectProgressReport>((project) => project.finishedInstances)}
                 render={(_text, item: APIProjectProgressReport) => ({
                   props: {
                     colSpan: 3,
                   },
                   children: (
-                    <StackedBarChart
-                      a={item.finishedInstances}
-                      b={item.activeInstances}
-                      c={item.pendingInstances}
-                    />
+                    item.totalInstances === 0 ? (
+                      <span>0</span>
+                    ) : (
+                      <StackedBarChart
+                        a={item.finishedInstances}
+                        b={item.activeInstances}
+                        c={item.pendingInstances}
+                      />
+                    )
                   ),
                 })}
               />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/project_progress_report_view.tsx` around
lines 175 - 207, Rows where item.totalInstances === 0 are treated as finished
(showing "100%") and passed as all-zero values into the StackedBarChart, which
can produce invalid rendering; update the render logic in the Column renderer
for finishedInstances to first check item.totalInstances === 0 and render an
explicit "0%" or a neutral Badge (and render StackedBarChart with a
guard/early-return or a special empty-state) instead of computing
Math.floor((100 * finishedInstances) / item.totalInstances) or passing a,b,c
when total is zero. Locate the Column with title Badge/finished and the render
function that returns colSpan/children (uses finishedInstances, activeInstances,
pendingInstances and StackedBarChart) and add the zero-total branch there.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx (1)

239-239: ⚠️ Potential issue | 🟡 Minor

Replace deprecated dropdownMatchSelectWidth with popupMatchSelectWidth.

The dropdownMatchSelectWidth prop was renamed to popupMatchSelectWidth in Ant Design v5+. Update the prop name to ensure the width constraint is applied correctly in v6.

🔧 Suggested fix
         <Select
           mode="multiple"
           allowClear
           autoFocus
           style={{ minWidth: 400 }}
-          dropdownMatchSelectWidth={false}
+          popupMatchSelectWidth={false}
           placeholder="Please select"
           onChange={handleChange}
           options={options}
           value={selectedOrganizationIds}
         />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx` at line 239,
Replace the deprecated Select prop dropdownMatchSelectWidth with
popupMatchSelectWidth in the ai_model_list_view.tsx JSX (look for the Select
component usage or the AIModelListView render/return where
dropdownMatchSelectWidth={false} is set); change the prop name only (keep the
value as-is, e.g., popupMatchSelectWidth={false}) to match Ant Design v5+/v6 API
so the width behavior is applied correctly.
🧹 Nitpick comments (3)
frontend/javascripts/admin/statistic/time_tracking_overview.tsx (1)

218-228: Consider removing extraneous whitespace in summary cells.

The {" "} patterns around values in summary cells appear to add unnecessary padding that could be handled via CSS if needed.

♻️ Suggested cleanup
         <Table.Summary.Cell index={2} align="right">
-          {" "}
-          {totalNumberOfTasksAndAnnotations}{" "}
+          {totalNumberOfTasksAndAnnotations}
         </Table.Summary.Cell>
         <Table.Summary.Cell index={3} align="right">
           {formatMilliseconds(totalTimeMs / totalNumberOfTasksAndAnnotations)}
         </Table.Summary.Cell>
         <Table.Summary.Cell index={4} align="right">
-          {" "}
-          {formatMilliseconds(totalTimeMs)}
+          {formatMilliseconds(totalTimeMs)}
         </Table.Summary.Cell>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/statistic/time_tracking_overview.tsx` around lines
218 - 228, Remove the extraneous JSX whitespace tokens surrounding values in the
summary cells: in the Table.Summary.Cell instances that render
totalNumberOfTasksAndAnnotations, formatMilliseconds(totalTimeMs /
totalNumberOfTasksAndAnnotations), and formatMilliseconds(totalTimeMs) remove
the {" "} entries so the cells only render the value; rely on CSS/padding for
spacing instead of literal JSX spaces.
frontend/javascripts/admin/tasktype/task_type_list_view.tsx (1)

113-121: Type mismatch between onPressEnter and handleSearch handler.

The @ts-expect-error comment masks a genuine type incompatibility. onPressEnter expects a KeyboardEventHandler but handleSearch is typed for ChangeEvent<HTMLInputElement>. While this works at runtime (both events have target.value), the type suppression is incorrect.

Consider either:

  1. Creating a separate handler for onPressEnter that doesn't require the event, or
  2. Removing onPressEnter since onChange already updates the search query in real-time
♻️ Suggested fix
       search={
         <Search
           allowClear
-          // `@ts-expect-error` ts-migrate(2322) FIXME: Type '(event: React.ChangeEvent<HTMLInputElement>)... Remove this comment to see the full error message
-          onPressEnter={handleSearch}
           onChange={handleSearch}
           value={searchQuery}
         />
       }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/tasktype/task_type_list_view.tsx` around lines 113
- 121, The onPressEnter prop is incorrectly assigned handleSearch (a
ChangeEvent<HTMLInputElement> handler) causing a TS type mismatch; either remove
the onPressEnter prop if realtime onChange is sufficient, or add a new
keyboard-specific handler (e.g., handlePressEnter) that matches
KeyboardEventHandler<HTMLInputElement> and delegates to the same search logic
(or calls handleSearch's core logic without requiring a ChangeEvent). Update the
Search usage to pass the new handlePressEnter for onPressEnter and keep
handleSearch for onChange, and remove the `@ts-expect-error` comment.
frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx (1)

22-22: Consider extracting Search destructuring outside the component.

Destructuring { Search } = Input inside the component body creates a new reference on each render. Move this outside the component or use Input.Search directly in the JSX.

♻️ Suggested refactor
+const { Search } = Input;
+
 export default function AiModelListView() {
-  const { Search } = Input;
   const activeUser = useWkSelector((state) => enforceActiveUser(state.activeUser));

Or use Input.Search directly at line 67:

-          <Search
+          <Input.Search
             allowClear
             onChange={(event) => setSearchQuery(event.target.value)}
             value={searchQuery}
           />
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx` at line 22, The
destructuring const { Search } = Input is done inside the component, causing a
new reference each render; move that destructure to module scope (above the
component) or replace all uses of Search in the JSX with Input.Search so the
reference is stable; locate the Search usage and the component in
ai_model_list_view.tsx (symbols: Search, Input, and the component function) and
either hoist const { Search } = Input to the top of the file or update JSX to
use Input.Search.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@frontend/javascripts/admin/statistic/time_tracking_detail_view.tsx`:
- Around line 27-31: The return type and local arrays use the removed global JSX
namespace (JSX.Element); update the file to import a proper React type and
replace occurrences: add an import type like `import type { ReactElement } from
"react"` (or `import type { JSX } from "react"`) and change the function's
return annotation and the local variables `taskRows` and `annotationRows` from
`JSX.Element[]` to `ReactElement[]` (or `JSX.Element[]` → `JSX.Element[]` with
the new import), and apply the same replacement pattern to the other listed
files (color_picker.tsx, layer_selection.tsx, mag_selection.tsx,
metadata_table.tsx, download_modal_view.tsx, brush_presets.tsx,
tool_buttons.tsx, bounding_box_selection.tsx,
bounding_box_selection_form_item.tsx, command_palette.tsx) so the code uses an
explicit imported React type instead of the global JSX namespace.

---

Outside diff comments:
In `@frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx`:
- Line 239: Replace the deprecated Select prop dropdownMatchSelectWidth with
popupMatchSelectWidth in the ai_model_list_view.tsx JSX (look for the Select
component usage or the AIModelListView render/return where
dropdownMatchSelectWidth={false} is set); change the prop name only (keep the
value as-is, e.g., popupMatchSelectWidth={false}) to match Ant Design v5+/v6 API
so the width behavior is applied correctly.

---

Nitpick comments:
In `@frontend/javascripts/admin/statistic/time_tracking_overview.tsx`:
- Around line 218-228: Remove the extraneous JSX whitespace tokens surrounding
values in the summary cells: in the Table.Summary.Cell instances that render
totalNumberOfTasksAndAnnotations, formatMilliseconds(totalTimeMs /
totalNumberOfTasksAndAnnotations), and formatMilliseconds(totalTimeMs) remove
the {" "} entries so the cells only render the value; rely on CSS/padding for
spacing instead of literal JSX spaces.

In `@frontend/javascripts/admin/tasktype/task_type_list_view.tsx`:
- Around line 113-121: The onPressEnter prop is incorrectly assigned
handleSearch (a ChangeEvent<HTMLInputElement> handler) causing a TS type
mismatch; either remove the onPressEnter prop if realtime onChange is
sufficient, or add a new keyboard-specific handler (e.g., handlePressEnter) that
matches KeyboardEventHandler<HTMLInputElement> and delegates to the same search
logic (or calls handleSearch's core logic without requiring a ChangeEvent).
Update the Search usage to pass the new handlePressEnter for onPressEnter and
keep handleSearch for onChange, and remove the `@ts-expect-error` comment.

In `@frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx`:
- Line 22: The destructuring const { Search } = Input is done inside the
component, causing a new reference each render; move that destructure to module
scope (above the component) or replace all uses of Search in the JSX with
Input.Search so the reference is stable; locate the Search usage and the
component in ai_model_list_view.tsx (symbols: Search, Input, and the component
function) and either hoist const { Search } = Input to the top of the file or
update JSX to use Input.Search.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 4ad115e6-9086-40b9-8f02-0ab57ba1d6a4

📥 Commits

Reviewing files that changed from the base of the PR and between 9908212 and 490bbe9.

📒 Files selected for processing (9)
  • frontend/javascripts/admin/job/job_list_view.tsx
  • frontend/javascripts/admin/project/project_list_view.tsx
  • frontend/javascripts/admin/statistic/available_tasks_report_view.tsx
  • frontend/javascripts/admin/statistic/project_progress_report_view.tsx
  • frontend/javascripts/admin/statistic/time_tracking_detail_view.tsx
  • frontend/javascripts/admin/statistic/time_tracking_overview.tsx
  • frontend/javascripts/admin/tasktype/task_type_list_view.tsx
  • frontend/javascripts/admin/team/team_list_view.tsx
  • frontend/javascripts/admin/voxelytics/ai_model_list_view.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • frontend/javascripts/admin/team/team_list_view.tsx

Copy link
Copy Markdown
Member

@philippotto philippotto left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice UI overhaul and clean up 👍

some things:

  • create script page looks weird:
    • shadow without margin (not sure if the shadow makes sense there at all)
    • very wide text fields
Image
  • create project page and create task type page, too
  • tasks page layout could be improved a bit. I highlighted the (imo) too-big whitespace and the too-wide fields
Image
  • create task page still has old style
  • the settings button in the project progress view does nothing for me

Comment on lines +6 to +11
function normalizeTitle(title: ReactNode) {
if (typeof title !== "string") {
return title;
}
return title.toUpperCase();
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a big fan of capslock case here. if you still want to keep it, I'd vote for additional css like font-weight: 100; letter-spacing: 6px; to make it less agressive. by the way, one could also use text-transform: uppercase; instead of the string modification.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added a textTransform.

Regarding font-weight and letter-spacing: This is the default antd-style for headings (<Typography.Title>). I don't want to overwrite that otherwise we lose styling, consistency all over the app again.

Comment on lines +107 to +109
if (!Array.isArray(selectedTeam) && selectedTeam != null) {
handleTeamChange(selectedTeam);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wasn't able to clear the team again. not sure if it worked before but this line probably prevents it.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure what you mean. The table is empty be default unless you select a team. There is not reason "clear" (unselect) a team to show an empty table again.

return isLoading ? null : (
<Alert title="Add more teams" description={teamMessage} type="info" showIcon />
);
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there's no replacement for this, right? wouldn't it fit well into the alerts prop for the admin page component?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the informational part has been moved to the page description part including a link to the docs. Having another link to trigger the TeamCreationModal in addition to the primary button did not add much value.

Notes:

- The view refreshes automatically in regular intervals and can also be refreshed manually.
- You can re-open the team filter panel at any time via the settings button.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah ok, then the button does something, but it won't hide the team selection UI again. either, the button should get disabled then or it should toggle the visibility.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Context: Since this report was displayed on a big monitor as a dashboard in the lab, we added the ability to hide the team selection filter a long time ago.

Comment on lines +57 to +61
- Per-user totals for annotation/task count and tracked time.
- Average time per task/annotation.
- Expandable rows for detailed per-user time-tracking entries.
- CSV export for the current overview.
- Per-user CSV export of detailed time spans.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

these are not sentences which is why I'd omit the full stop. also potentially lower-case the first word

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done


## Create a Script

1. Open the `Scripts` screen in the administration section.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we typically use the word screen in the UI? I'd rather use "page".

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

screen: 76x (including "screenshot", and screen aka computer monitor)
page: 120x

I have changed a few other pages as well

Comment on lines +25 to +34
(function () {
const toggleColor = () => {
const layer = window.webknossos.getCurrentLayer();
if (layer) {
layer.setColor(layer.getColor() === "red" ? "blue" : "red");
}
};

window.webknossos.registerKeyboardShortcut("t", toggleColor);
})();
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think that this is a valid script.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

got point. I am meant include a simple example but then lost track. I updated it.

## Best Practices

- Keep scripts focused and task-specific.
- Version changes in your Gist history.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

how about linking github gist in case somebody doesn't know it?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, at the top of the page for the first mention of gists

2. Click `Add Script`.
3. Fill in:
- `Script Name`: Human-readable label shown in selectors.
- `Gist URL`: URL of the script source, a public GitHub Gist that your setup can access.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does it need to be a gist url? it's not clear whether the second part of the sentence refines the first or whether it is an alternative.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, Gist URL
Screenshot 2026-03-19 at 10 28 29

hotzenklotz and others added 2 commits March 19, 2026 09:33
…pe_dropdown.tsx

Co-authored-by: Philipp Otto <philippotto@users.noreply.github.com>
@hotzenklotz hotzenklotz enabled auto-merge (squash) March 27, 2026 13:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants