Release v0.2.0 — Collaborative Ontology Editor#1
Conversation
…ts), error boundary, API retry logic, and i18n locale detection - Fix auth.ts type safety: replace `as any` with `as User` - Add GitHub Actions CI: lint, type-check, test, build on Node.js 22 - Add ESLint flat config with no-explicit-any warn and no-unused-vars error - Add Zod-based server/client environment variable validation - Configure Vitest with jsdom, React plugin, and path aliases - Add 26 passing tests for utility functions and API client - Add React ErrorBoundary component with retry support - Add 30s request timeout and 5xx retry with exponential backoff to API client - Implement Accept-Language header detection and cookie-based locale persistence Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds WebProtege-style entity creation via source injection: generates valid Turtle snippets (all 5 OWL entity types) and appends them to the editor buffer. The tree optimistically shows new classes immediately, with the detail panel gracefully falling back to tree-node data for unsaved entities. Source and tree views stay in sync via getValue() ref and view-switch syncing. New files: - lib/ontology/iriGeneration.ts (base62 UUID, pattern detection, IRI gen) - lib/ontology/turtleSnippetGenerator.ts (Turtle output for all types) - components/editor/AddEntityDialog.tsx (label, type, IRI fields) - 41 new tests across 2 test files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and page decomposition Phase 1 of Standard Mode implementation: - Create Zustand editorModeStore with mode (standard/developer) and theme (light/dark/system) preferences persisted to localStorage - Switch Tailwind dark mode from media queries to class-based selectors - Add inline theme script in layout.tsx to prevent flash of wrong theme - Add ModeSwitcher component (segmented control in editor header) - Add ThemeToggle component (light/dark/system in global header) - Decompose 1127-line editor page into orchestrator + DeveloperEditorLayout + StandardEditorLayout - Add Editor Preferences section to Settings page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Right-click context menu on tree nodes with Add Subclass, Copy IRI, View in Source (developer mode), and Delete class with confirmation. Toast notification system wired into providers. Copy IRI button in detail panel header. Optimistic tree removal on delete. Fix canEdit to default authenticated users without explicit role to edit access. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ons, and relationships (Phases 3 + 3.5) Implements structured editing of OWL classes through the detail panel, routing saves through Turtle source manipulation since the backend class endpoint only supports GET. Adds annotation editing with inline property adder, relationship section for IRI-valued properties (rdfs:seeAlso), entity search combobox, resizable panel divider, entity tabs, and async label resolution for relationship targets. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fields are now always editable when canEdit — no Edit/Save/Cancel buttons. Blur saves instantly to a Zustand draft store (localStorage-persisted), and navigating to a different class flushes the draft as a single git commit. Tree nodes show amber dot badges for classes with uncommitted drafts, and a status bar shows draft/saving/saved/error state. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and label resolution fixes - Default read-only detail panel with explicit Edit Item / Cancel buttons - Continuous editing toggle (persisted in Zustand store) auto-enters edit mode across class navigation - Cancel overrides continuous editing for the current class via cancelledIriRef - discardDraft() and onError callback added to useAutoSave hook - Tree preserves expansion state after class updates (updateNodeLabel instead of loadRootClasses) - Fix layout shift: always-rendered + button in tree rows reserves space - Resolve rdfs:label for individuals/properties via searchEntities fallback - Patch edit-mode relationship labels when async resolution completes - Continuous editing preference added to Settings page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nt header - Add Classes/Properties/Individuals tab bar to Developer layout tree panel - Remove duplicative text header below tab bar in Standard layout - Extract EntityPlaceholderDetail into shared component used by both layouts - Both views now have identical entity tab structure with compact toolbar Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…xpand/collapse Refactor tree UI across all entity tabs (Classes, Properties, Individuals) to use a shared EntityTree/EntityTreeNode renderer with FOLIO-inspired patterns: leaf dots instead of C/P/I badges, root emphasis, double-click expand, keyboard navigation (arrow keys + Enter/Space), and filtered tree search with ancestor-path context. Extract reusable EntityTreeToolbar with expand-all/collapse-all buttons and useTreeSearch hook to eliminate duplicated search state across both layouts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… border - Place leaf dot inside same 16px container as chevron so labels align at every depth - Add permanent transparent left border on root items to prevent 2px shift on selection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… aligned column layout Implements full detail view and edit capability for Properties and Individuals, parsing metadata from Turtle source. Includes shared Turtle utilities, block parser, entity detail extractors, updaters, generic auto-save hook, IRI label resolution, and property assertion sections. Read-only multi-row sections (Annotations, Object/Data Properties) use flat row layout with field name badges aligned in the section heading column. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…l panels Add langToFlag() utility and shared LanguageFlag component that maps ISO language tags to flag emojis (with grey badge fallback for unmappable codes). Applied across all view-mode and edit-mode locations in Class, Property, and Individual panels. Primary Label omits flags in view mode since English is the assumed default. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements Phases A+B (suggestion submission) and Phase D (review & admin): - Suggester role with session-based editing, auto-save to suggestion branch, and PR submission - Review page for editors/admins with diff viewing, approve/reject/request-changes actions - Feedback loop: suggesters see reviewer feedback and can resume editing changed-requested sessions - Unified notification system with polling hook, type-based icons, and mark-as-read - Notification bell rewritten to use useNotifications hook with flat notification list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drag a class node onto another class to reparent it, with optimistic UI updates, undo toast, auto-expand on hover, root drop zone, Alt-key ADD mode (multi-parent), and cycle detection. Uses @dnd-kit/core with a pointer-tracking overlay for reliable cursor alignment. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Phase 6A: Entity graph visualization using React Flow + ELK.js with custom nodes/edges, lazy-loaded via next/dynamic. Supports TB/LR layout, node expansion, and dark mode. Phase 6B: Global keyboard shortcuts (Ctrl+S save, Ctrl+N new entity, Ctrl+K search, ? help dialog) with Mac/Windows support and input/Monaco/dialog suppression. Phase 6C: Accessibility improvements including skip links, ARIA live regions via ScreenReaderAnnouncer, aria-labels on icon buttons and form inputs, aria-activedescendant on tree, reduced motion and high contrast media queries. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
SessionGuard: detect RefreshAccessTokenError globally and force re-authentication when the Zitadel refresh token is dead, preventing stale-token API failures across all pages. Graph: include seeAlso/isDefinedBy IRI targets when expanding neighbor nodes so relationship edges render in the visualization. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…h Edit button Added headerActions prop to ClassDetailPanel and moved the Graph button from an absolute-positioned overlay into the header flex row so it sits side-by-side with the Edit/Cancel button. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
LanguageFlag now always renders a fixed h-5 w-5 container regardless of whether a flag emoji can be resolved, keeping the flag column width consistent across all annotation rows. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace single expand-all/collapse-all icon buttons with split-button groups offering incremental control: expand one level (default) with expand-all as secondary, and collapse all (default) with collapse one level as secondary. Includes disabled states, loading spinner, and a dismissible tip for first-time users. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New users joining via join request approval, manual member addition, or no-role fallback now default to suggester instead of editor. Existing editors keep their roles. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Release v0.2.0 — collaborative ontology editing, suggestion workflow, graph visualization, keyboard shortcuts, accessibility, and 111 tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Graph nodes showed opaque IRI fragments when resolveLabel() couldn't find a label. This adds three fallback tiers: tree label hints from in-memory class tree nodes, a label resolution pass for depth-3+ relationship targets, and search API lookups by local name for non-class entities (individuals/properties) that fail getClassDetail. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Trace every displayed class node's ancestry back to root via the ancestors API so nodes no longer appear floating. Color-code individuals (pink) and properties (blue) with letter badges, and add a collapsible legend showing all node and edge types. Filter out owl:Thing from the graph since it adds no informational value. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Nodes whose only parent is owl:Thing are now classified as root nodes so they receive the amber visual treatment instead of plain class styling. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Pass tree-derived label hints through layout components to IndividualDetailPanel and PropertyDetailPanel, so useIriLabels can resolve class names without failing API calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When the auth provider (Zitadel) is restarting, the error page now shows an amber spinner with a 10s countdown and retries up to 6 times instead of dead-ending with a static "Configuration Error" message. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… indicator) Enables projects to track external GitHub repos for upstream changes. Includes config CRUD, job polling, notification types, and sync status indicator in the editor toolbar. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…emantic search Includes cross-references, similar concepts, entity history, delete impact analysis, smart parent suggestions, semantic search toggle, health check consistency/duplicates tabs, project analytics page, and atomization plan. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
In suggestion mode, property and individual updates were bypassing the suggestion branch and committing directly. Add handleSuggestPropertyUpdate and handleSuggestIndividualUpdate handlers that save via suggestionSession.saveToSession, matching the class update pattern. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add explicit assertion that the focus→parent subClassOf edge is preserved even when child-cap filtering prunes other siblings. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add test verifying that a node attached as a child in one path is not duplicated as a root when another path has empty ancestors (failed ancestor fetch). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Additional Fixes (Round 2)Addressed CodeRabbit re-review findings, CodeQL security alert, and CI type-check failure with 16 individual commits: CI / Security
CodeRabbit — Critical / Major
Tests
Verification: 90 tests pass, 0 lint errors, 0 type-check errors. |
There was a problem hiding this comment.
Actionable comments posted: 3
♻️ Duplicate comments (2)
components/editor/IndividualDetailPanel.tsx (2)
268-272:⚠️ Potential issue | 🟠 MajorKeep the trailing empty label row on restore and edit.
The label-row invariant is still broken here: restored drafts set
d.labelsverbatim, andupdateLabel/removeLabelstop reapplyingensureTrailingEmpty. Once the last blank row is filled, or a draft is restored without one, there is no path to add another label.💡 Suggested fix
- setEditLabels(d.labels); + setEditLabels(ensureTrailingEmpty(d.labels.map((l) => ({ ...l })))); const updateLabel = useCallback((index: number, field: "value" | "lang", val: string) => { - setEditLabels((prev) => prev.map((l, i) => (i === index ? { ...l, [field]: val } : l))); + setEditLabels((prev) => + ensureTrailingEmpty(prev.map((l, i) => (i === index ? { ...l, [field]: val } : l))) + ); }, []); const removeLabel = useCallback((index: number) => { - setEditLabels((prev) => prev.filter((_, i) => i !== index)); + setEditLabels((prev) => ensureTrailingEmpty(prev.filter((_, i) => i !== index))); requestAnimationFrame(() => triggerSave()); }, [triggerSave]);Also applies to: 292-297
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/IndividualDetailPanel.tsx` around lines 268 - 272, Restored drafts currently assign d.labels verbatim to state, and label mutators (updateLabel/removeLabel) stop reapplying ensureTrailingEmpty, which breaks the invariant of keeping a trailing empty label row; update the restore path (where restoredDraft is cast to IndividualDraftEntry and setEditLabels is called) to pass labels through ensureTrailingEmpty (e.g., setEditLabels(ensureTrailingEmpty(d.labels))) and likewise modify the label mutation functions (updateLabel and removeLabel) to always call ensureTrailingEmpty before setting state so the trailing empty label is preserved after edits and restores.
173-196:⚠️ Potential issue | 🟠 MajorDon't serialize
seeAlso/isDefinedBytwice.
relationshipAnnotationsstill includes every relationship group, and the same two groups are also sent viaseeAlsoIrisandisDefinedByIris. That leaves the updater with two sources of truth for the same triples and can duplicate them on save.💡 Suggested fix
+ const IS_DEFINED_BY_IRI = "http://www.w3.org/2000/01/rdf-schema#isDefinedBy"; + const relationshipAnnotations: AnnotationUpdate[] = draft.relationships - .filter((g) => g.targets.length > 0) + .filter( + (g) => + g.targets.length > 0 && + g.property_iri !== SEE_ALSO_IRI && + g.property_iri !== IS_DEFINED_BY_IRI + ) .map((g) => ({ property_iri: g.property_iri, values: g.targets.map((t) => ({ value: t.iri, lang: "" })), })); @@ seeAlsoIris: draft.relationships .filter((g) => g.property_iri === SEE_ALSO_IRI) .flatMap((g) => g.targets.map((t) => t.iri)), isDefinedByIris: draft.relationships - .filter((g) => g.property_iri === "http://www.w3.org/2000/01/rdf-schema#isDefinedBy") + .filter((g) => g.property_iri === IS_DEFINED_BY_IRI) .flatMap((g) => g.targets.map((t) => t.iri)),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/IndividualDetailPanel.tsx` around lines 173 - 196, relationshipAnnotations is built from all draft.relationships and ends up duplicating seeAlso and isDefinedBy because those same groups are also passed via seeAlsoIris and isDefinedByIris; modify the relationshipAnnotations creation in IndividualDetailPanel (the relationshipAnnotations variable) to filter out groups whose property_iri equals SEE_ALSO_IRI or "http://www.w3.org/2000/01/rdf-schema#isDefinedBy" before mapping, so only non-seeAlso/non-isDefinedBy relationships are serialized into annotations while seeAlsoIris and isDefinedByIris remain the sole sources for those predicates.
🧹 Nitpick comments (6)
components/editor/standard/RelationshipSection.tsx (5)
429-442: Add accessible label to the entity search input.Same accessibility concern as the property picker — this input needs an accessible name for screen readers.
♿ Proposed accessibility fix
<input type="text" value={query} onChange={(e) => setQuery(e.target.value)} onFocus={() => setIsFocused(true)} onKeyDown={(e) => { if (e.key === "Escape") { setIsFocused(false); (e.target as HTMLInputElement).blur(); } }} placeholder="Search entities to add..." + aria-label="Search entities to add" className="flex-1 rounded-md border border-dashed border-slate-300 bg-white px-2.5 py-1.5 text-sm placeholder:text-slate-400 focus:border-primary-500 focus:border-solid focus:outline-none focus:ring-1 focus:ring-primary-500 dark:border-slate-600 dark:bg-slate-700 dark:text-white dark:placeholder:text-slate-500" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/RelationshipSection.tsx` around lines 429 - 442, The entity search input in RelationshipSection lacks an accessible name; add one by providing an aria-label or linking a visible/visually-hidden <label> to the input (use the input's id) so screen readers announce it. Locate the input element using the RelationshipSection component and the variables/handlers query, setQuery and setIsFocused, then add a descriptive aria-label (e.g., "Search entities to add") or an associated label element and ensure focus/escape behavior remains unchanged.
257-263: Improve dropdown button accessibility.The property picker button lacks ARIA attributes to communicate dropdown state to assistive technologies.
♿ Proposed accessibility fix
<button onClick={() => setIsOpen(!isOpen)} + aria-haspopup="listbox" + aria-expanded={isOpen} className="flex w-full items-center gap-1 rounded-md border border-slate-300 bg-white px-2 py-1 text-left text-xs font-medium text-slate-700 hover:border-slate-400 dark:border-slate-600 dark:bg-slate-700 dark:text-slate-300 dark:hover:border-slate-500" >🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/RelationshipSection.tsx` around lines 257 - 263, The dropdown toggle button lacks accessibility attributes and should announce its role and state: update the button (the element using onClick={() => setIsOpen(!isOpen)} and rendering currentLabel and <ChevronDown />) to include type="button", aria-haspopup="listbox" (or "popup" if the panel is a menu), aria-expanded={isOpen}, and aria-controls pointing to the id of the dropdown panel; ensure the dropdown panel element has a matching id and appropriate role (e.g., role="listbox" or role="menu") so assistive tech can associate the button with the controlled popup.
240-241: Consider logging search errors for observability.The empty catch blocks silently swallow API errors. While the UI gracefully falls back to empty results, logging these errors would help with debugging in production.
🔍 Example improvement
- } catch { - if (!cancelled) setApiResults([]); + } catch (error) { + console.error("Property search failed:", error); + if (!cancelled) setApiResults([]);Also applies to: 396-397
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/RelationshipSection.tsx` around lines 240 - 241, The catch blocks currently swallow errors; modify both catch clauses (the one surrounding setApiResults([]) at the try/catch near setApiResults and the similar block around lines 396-397) to accept an error parameter (e.g., catch (err)) and log that error for observability before falling back to setApiResults([]) — use the app's logger if available or console.error with a short contextual message mentioning RelationshipSection and the operation, and preserve the cancelled check logic.
269-279: Add accessible label to the search input.The search input lacks an accessible name. Screen readers won't convey its purpose to users.
♿ Proposed accessibility fix
<input ref={inputRef} type="text" value={query} onChange={(e) => setQuery(e.target.value)} onKeyDown={(e) => { if (e.key === "Escape") setIsOpen(false); }} placeholder="Search properties..." + aria-label="Search properties" className="flex-1 bg-transparent text-xs text-slate-900 placeholder:text-slate-400 focus:outline-none dark:text-white dark:placeholder:text-slate-500" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/RelationshipSection.tsx` around lines 269 - 279, The search input in RelationshipSection.tsx (the input element using ref inputRef, value={query}, onChange={setQuery}) has no accessible name; add an accessible label by either giving the input an id and associating a <label htmlFor="..."> (visually hidden if needed) or adding an aria-label/aria-labelledby attribute such as aria-label="Search properties" so screen readers can announce its purpose; ensure you update the input element where setQuery and setIsOpen are used to include the chosen accessible label.
434-439: Consider adding keyboard navigation for dropdown results.Both dropdowns support Escape to close but lack arrow key navigation (Up/Down) and Enter to select. This would improve keyboard accessibility for users who can't use a mouse.
Also applies to: 274-276
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/RelationshipSection.tsx` around lines 434 - 439, The onKeyDown handler currently only handles Escape (setting setIsFocused(false) and blurring the input) and needs to support ArrowUp/ArrowDown and Enter to enable keyboard navigation: add a highlightedIndex state (e.g., highlightedIndex, setHighlightedIndex) used by the dropdown render to highlight items, update the two onKeyDown handlers (the one shown and the similar handler around lines 274-276) to intercept "ArrowDown"/"ArrowUp" to increment/decrement highlightedIndex (with bounds or wrap-around) and call e.preventDefault(), and handle "Enter" to select the item at highlightedIndex (invoke the existing selection callback used by click handlers) then close the dropdown (setIsFocused(false)) and blur the input; ensure the highlightedIndex resets when the dropdown opens or the results list changes.components/editor/standard/PropertyTree.tsx (1)
59-84: Property type classification relies on unreliable IRI pattern heuristics instead of API-provided RDF types.The code infers property types (Object, Data, Annotation) from IRI substrings (e.g.,
iri.includes("objectproperty")). However,EntitySearchResultfrom the API provides only a genericentity_type: "property"field—no subtype information exists. This means properties with standard IRIs likehttp://purl.org/dc/...will incorrectly default to "Object Properties" regardless of their actual RDF type (lines 80-82).The codebase defines proper OWL property types elsewhere (see
turtleSnippetGenerator.ts). The backend API should be enhanced to return the actual RDF property type in the search response, or this limitation should be clearly documented for users expecting correct categorization.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/standard/PropertyTree.tsx` around lines 59 - 84, Update the classification in PropertyTree.tsx to stop relying solely on IRI substring heuristics and instead use the API-provided RDF property type (add/expect a field on EntitySearchResult such as rdfType, rdf_type or propertyType). In the loop over response.results, switch on prop.rdfType values (e.g., "owl:ObjectProperty", "owl:DatatypeProperty", "owl:AnnotationProperty") to push into objectProps, dataProps, or annotationProps; if that field is missing, fall back to the existing IRI heuristics but emit a console.warn or comment documenting the limitation and referencing EntitySearchResult so callers/backend can be updated to return the RDF type. Ensure variable names objectProps, dataProps, annotationProps and the response.results loop are the only changed locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@__tests__/lib/graph/buildGraphData.test.ts`:
- Around line 232-242: The test builds a Map called resolved with the focusIri
entry inserted before childDetails which makes the pruning assertion pass due to
Map insertion order; change the construction so the focus entry is added after
the spread of childDetails (or otherwise randomize/order differently) so that
resolved = new Map([...childDetails, [focusIri, makeClassDetail({...})]])
ensures the cap-pruning test for subClassOf edges verifies that the focus edge
is preserved intentionally; update references to resolved, focusIri,
childDetails, and makeClassDetail accordingly.
In `@app/projects/`[id]/editor/page.tsx:
- Around line 879-887: When switching branches in handleBranchChange, also
discard the active suggestion session to avoid carrying stale session state;
call suggestionSession.discardSession() (or the appropriate discard method on
the suggestionSession object) before clearing source state and updating router,
and reset any session-related state like changesCount and entitiesModified (or
their setters) and beacon/context references so the new branch starts with a
fresh session context; update handleBranchChange to invoke the discard call and
session-state resets alongside setActiveBranch, setSourceContent,
setSourceError, setSourceIriIndex, and preloadStartedRef.current assignment.
In `@components/editor/IndividualDetailPanel.tsx`:
- Around line 240-248: The current useEffect in IndividualDetailPanel.tsx calls
flushToGit after individualIri has already changed, causing flushToGit (from
useEntityAutoSave.ts) to key the draft by the new IRI; fix by invoking the flush
for the previous entity before the prop switch: either move the flushToGit()
call into the cleanup function of that effect so it runs while
prevIriRef.current still refers to the old individual, or update flushToGit to
accept an explicit IRI/snapshot and call flushToGit(prevIriRef.current) before
updating prevIriRef/current state (ensure you still set
editInitializedRef.current = false, setIsEditing(false), and
cancelledIriRef.current = null after flushing).
---
Duplicate comments:
In `@components/editor/IndividualDetailPanel.tsx`:
- Around line 268-272: Restored drafts currently assign d.labels verbatim to
state, and label mutators (updateLabel/removeLabel) stop reapplying
ensureTrailingEmpty, which breaks the invariant of keeping a trailing empty
label row; update the restore path (where restoredDraft is cast to
IndividualDraftEntry and setEditLabels is called) to pass labels through
ensureTrailingEmpty (e.g., setEditLabels(ensureTrailingEmpty(d.labels))) and
likewise modify the label mutation functions (updateLabel and removeLabel) to
always call ensureTrailingEmpty before setting state so the trailing empty label
is preserved after edits and restores.
- Around line 173-196: relationshipAnnotations is built from all
draft.relationships and ends up duplicating seeAlso and isDefinedBy because
those same groups are also passed via seeAlsoIris and isDefinedByIris; modify
the relationshipAnnotations creation in IndividualDetailPanel (the
relationshipAnnotations variable) to filter out groups whose property_iri equals
SEE_ALSO_IRI or "http://www.w3.org/2000/01/rdf-schema#isDefinedBy" before
mapping, so only non-seeAlso/non-isDefinedBy relationships are serialized into
annotations while seeAlsoIris and isDefinedByIris remain the sole sources for
those predicates.
---
Nitpick comments:
In `@components/editor/standard/PropertyTree.tsx`:
- Around line 59-84: Update the classification in PropertyTree.tsx to stop
relying solely on IRI substring heuristics and instead use the API-provided RDF
property type (add/expect a field on EntitySearchResult such as rdfType,
rdf_type or propertyType). In the loop over response.results, switch on
prop.rdfType values (e.g., "owl:ObjectProperty", "owl:DatatypeProperty",
"owl:AnnotationProperty") to push into objectProps, dataProps, or
annotationProps; if that field is missing, fall back to the existing IRI
heuristics but emit a console.warn or comment documenting the limitation and
referencing EntitySearchResult so callers/backend can be updated to return the
RDF type. Ensure variable names objectProps, dataProps, annotationProps and the
response.results loop are the only changed locations.
In `@components/editor/standard/RelationshipSection.tsx`:
- Around line 429-442: The entity search input in RelationshipSection lacks an
accessible name; add one by providing an aria-label or linking a
visible/visually-hidden <label> to the input (use the input's id) so screen
readers announce it. Locate the input element using the RelationshipSection
component and the variables/handlers query, setQuery and setIsFocused, then add
a descriptive aria-label (e.g., "Search entities to add") or an associated label
element and ensure focus/escape behavior remains unchanged.
- Around line 257-263: The dropdown toggle button lacks accessibility attributes
and should announce its role and state: update the button (the element using
onClick={() => setIsOpen(!isOpen)} and rendering currentLabel and <ChevronDown
/>) to include type="button", aria-haspopup="listbox" (or "popup" if the panel
is a menu), aria-expanded={isOpen}, and aria-controls pointing to the id of the
dropdown panel; ensure the dropdown panel element has a matching id and
appropriate role (e.g., role="listbox" or role="menu") so assistive tech can
associate the button with the controlled popup.
- Around line 240-241: The catch blocks currently swallow errors; modify both
catch clauses (the one surrounding setApiResults([]) at the try/catch near
setApiResults and the similar block around lines 396-397) to accept an error
parameter (e.g., catch (err)) and log that error for observability before
falling back to setApiResults([]) — use the app's logger if available or
console.error with a short contextual message mentioning RelationshipSection and
the operation, and preserve the cancelled check logic.
- Around line 269-279: The search input in RelationshipSection.tsx (the input
element using ref inputRef, value={query}, onChange={setQuery}) has no
accessible name; add an accessible label by either giving the input an id and
associating a <label htmlFor="..."> (visually hidden if needed) or adding an
aria-label/aria-labelledby attribute such as aria-label="Search properties" so
screen readers can announce its purpose; ensure you update the input element
where setQuery and setIsOpen are used to include the chosen accessible label.
- Around line 434-439: The onKeyDown handler currently only handles Escape
(setting setIsFocused(false) and blurring the input) and needs to support
ArrowUp/ArrowDown and Enter to enable keyboard navigation: add a
highlightedIndex state (e.g., highlightedIndex, setHighlightedIndex) used by the
dropdown render to highlight items, update the two onKeyDown handlers (the one
shown and the similar handler around lines 274-276) to intercept
"ArrowDown"/"ArrowUp" to increment/decrement highlightedIndex (with bounds or
wrap-around) and call e.preventDefault(), and handle "Enter" to select the item
at highlightedIndex (invoke the existing selection callback used by click
handlers) then close the dropdown (setIsFocused(false)) and blur the input;
ensure the highlightedIndex resets when the dropdown opens or the results list
changes.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 16040f06-e982-413e-83cf-1ad1933cfc81
📒 Files selected for processing (9)
__tests__/lib/graph/buildGraphData.test.ts__tests__/lib/hooks/useFilteredTree.test.tsapp/projects/[id]/editor/page.tsxapp/projects/[id]/suggestions/review/page.tsxauth.tscomponents/editor/IndividualDetailPanel.tsxcomponents/editor/standard/PropertyTree.tsxcomponents/editor/standard/RelationshipSection.tsxlib/hooks/useFilteredTree.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- app/projects/[id]/suggestions/review/page.tsx
- tests/lib/hooks/useFilteredTree.test.ts
- auth.ts
The child-cap filter dropped the focus node's subClassOf edge when it was processed after 20 siblings. Always keep edges involving the focus node so it's never pruned from the graph. Updated the test to insert the focus node after siblings, verifying the fix is order-independent. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
handleBranchChange now calls suggestionSession.discardSession() so that stale session state (sessionId, changesCount, entitiesModified) doesn't persist across branch switches. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Move flushToGit into a cleanup function so it runs with the previous entity's closure before React switches to the new individualIri. This prevents the old entity's draft from being keyed by the new IRI and ensures the previous entity's edits are committed correctly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Additional Fixes (Round 3)Addressed 3 CodeRabbit re-review findings with individual commits: Bug Fixes
Verification: 90 tests pass, 0 lint errors, 0 type-check errors. |
There was a problem hiding this comment.
♻️ Duplicate comments (1)
components/editor/IndividualDetailPanel.tsx (1)
297-303:⚠️ Potential issue | 🟡 MinorLabel editor doesn't maintain trailing empty row during edits.
updateLabeldoesn't callensureTrailingEmpty, unlikeupdateCommentandupdateDefinition. Once the user types in the last label row, no new empty row is added, preventing additional labels from being created.🐛 Proposed fix
const updateLabel = useCallback((index: number, field: "value" | "lang", val: string) => { - setEditLabels((prev) => prev.map((l, i) => (i === index ? { ...l, [field]: val } : l))); + setEditLabels((prev) => ensureTrailingEmpty(prev.map((l, i) => (i === index ? { ...l, [field]: val } : l)))); }, []);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@components/editor/IndividualDetailPanel.tsx` around lines 297 - 303, updateLabel currently updates setEditLabels without ensuring a trailing empty row, so typing into the last label won't create a new empty row; mirror the pattern used by updateComment/updateDefinition by invoking ensureTrailingEmpty when labels change—either call ensureTrailingEmpty inside the setEditLabels updater (after computing the new labels) or call it in a requestAnimationFrame callback just like removeLabel does; reference updateLabel, ensureTrailingEmpty, setEditLabels, and triggerSave when making the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@components/editor/IndividualDetailPanel.tsx`:
- Around line 297-303: updateLabel currently updates setEditLabels without
ensuring a trailing empty row, so typing into the last label won't create a new
empty row; mirror the pattern used by updateComment/updateDefinition by invoking
ensureTrailingEmpty when labels change—either call ensureTrailingEmpty inside
the setEditLabels updater (after computing the new labels) or call it in a
requestAnimationFrame callback just like removeLabel does; reference
updateLabel, ensureTrailingEmpty, setEditLabels, and triggerSave when making the
change.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 3b4dd340-dec7-4423-8fba-89c693ddc06d
📒 Files selected for processing (4)
__tests__/lib/graph/buildGraphData.test.tsapp/projects/[id]/editor/page.tsxcomponents/editor/IndividualDetailPanel.tsxlib/graph/buildGraphData.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- lib/graph/buildGraphData.ts
- tests/lib/graph/buildGraphData.test.ts
Wrap updateLabel's setEditLabels with ensureTrailingEmpty so typing into the last label row creates a new empty row, matching the pattern used by updateComment and updateDefinition. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Additional Fix (Round 4)
Verification: 90 tests pass, 0 lint errors, 0 type-check errors. |
The startup script had a non-standard default of 53000 while all other configuration (Zitadel redirect URIs, AUTH_URL, docs) assumed port 3000. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add comprehensive v0.2.0 changelog entry with new features, improvements, accessibility, testing, and bug fix categories. Fix v0.1.0 date from 2025-06-15 to 2026-02-18 (verified via git tag). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add suggestion workflow, auto-save, editor modes, graph visualization, keyboard shortcuts, and suggester role to the docs page. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Scalar uses body.dark-mode/light-mode classes internally and its forceDarkModeState prop is not reactive after mount. Use a MutationObserver to mirror the html.dark class onto body classes, and hide Scalar's own toggle to avoid conflicts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use index-suffixed keys to handle OWL multiple inheritance where the same IRI can appear under different parent nodes in the tree. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds missing @testing-library/dom and related transitive dependencies that were causing npm ci to fail in CI. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Poll the embedding status endpoint every 2 seconds while a generation job is in progress, so the entities-embedded count and progress bar update live. Also resumes polling on page load if a job is already running. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When stdin is not a terminal (piped, redirected, or run from a script), automatically enable force mode to avoid infinite interactive prompts on port conflicts. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…onfig Pre-populates repo owner, name, branch, and file path from active GitHub integration when no upstream sync config exists. Also updates the hook to handle 200/null responses instead of catching 404 errors. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add github_hook_id to GitHubIntegration type and setupWebhook API method - Add webhook frequency option to upstream sync types - Rearrange GitHub integration info boxes in project settings - Enhance WebhookConfigPanel with auto-setup status and fallback to manual - Add upstream sync info box when webhooks are enabled - Add admin:repo_hook scope guidance in user settings page Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
When webhooks are enabled and upstream sync is webhook-driven, repo fields are read-only with dimmed styling and an explanatory note is shown in both configured and editing views. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
@damienriehl I added CodeRabbit which I have been using on a few other projects, it does a wonderful job of reviewing and nit-picking. I then feed CodeRabbit's review issues back into Claude Code. I find that it is best to address each issue with a commit but specifically instruct Claude not to push until all CodeRabbit issues have been addressed, because CodeRabbit triggers a new review every time you push but it also implements rate-limiting, so with many pushes it'll just quit. So best to make all the commits first, then push them all at once when they're ready. I either copy the "Prompt for AI Agents" section for each issue, one at a time; or, at the risk of perhaps consuming a few more tokens but speeding up the process, I tell Claude to directly read all the issues raised by CodeRabbit's review on the PR (I have the GitHub CLI installed, which Claude uses to read the PR), but again instructing specifically to address each issue with one commit per fix but not push until all have been addressed. So now, linting and tests are passing! |
|
I added a webhooks UI to the GitHub integration, which should be a valid alternative to "Upstream Source Tracking". The idea is that as soon as webhooks are enabled and installed, "Upstream Source Tracking" will be readonly; if webhooks are not enabled, "Upstream Source Tracking" can be set but will default to the same GitHub repo when GitHub integration is active. |
OntoKit — Comprehensive Change Summary
Overview
This PR transforms OntoKit from a basic ontology viewer into a full-featured collaborative ontology editor. The changes span both the frontend (Next.js) and backend (FastAPI) and can be grouped into four themes:
Editing experience — Users can now create, edit, and reparent ontology entities through structured forms rather than raw Turtle source. A WebProtege-style auto-save system prevents data loss: edits are drafted locally on blur and committed to git on navigation. Classes open read-only by default to prevent accidental changes, with an explicit "Edit Item" button and an optional continuous-editing toggle. Drag-and-drop reparenting supports OWL multi-parent semantics with cycle detection and undo.
Collaboration — A new "suggester" role allows non-editors to propose changes. Suggesters edit on dedicated branches; their work is auto-saved via
sendBeaconand can be submitted as pull requests for editor review. Editors can approve, reject, or request changes through a built-in review page, with notifications keeping everyone informed. New users now default to the suggester role to ensure all initial contributions go through review.Visualization & navigation — An interactive graph view (React Flow + ELK layout) renders entity relationships with expandable nodes and edge-type styling. Full ancestry tracing ensures every node connects back to root instead of floating. Entity types (classes, individuals, properties) are color-coded with letter badges and a collapsible legend. A shared FOLIO-style tree renderer supports keyboard navigation, filtered search, and multi-level expand/collapse across Classes, Properties, and Individuals. Language tags display as flag emojis for a cleaner, more visual presentation.
Quality & accessibility — Both repos gained comprehensive test suites (111 tests total). The frontend added CI/CD, ESLint strict rules, Zod environment validation, error boundaries, and API retry logic. The backend gained lifespan handlers, JWT role extraction, full-text search, rate limiting, security headers, and custom exception types. Accessibility improvements include skip links, ARIA live regions, screen reader announcements for drag-and-drop, keyboard shortcuts, and reduced-motion/high-contrast media queries.
Table of Contents
1. Infrastructure & Code Quality
Commit:
a3c8d33(web)Commit:
1cab6c0(api — infrastructure portion)Frontend (ontokit-web)
.github/workflows/ci.yml): lint → type-check → test → build, runs on push to main and PRs, Node.js 22, cachednode_moduleseslint.config.mjswith@typescript-eslint/no-explicit-any: "warn"and stricter ruleslib/env.tsuses Zod to validate required env vars at build time with helpful error messages; separates server-side and client-side varsvitest.config.ts) withhappy-dom, path aliases, coverage thresholds; 26 tests covering:getLocalName,getNamespace,getPreferredLabel,formatDate,debounce,generateId)api.get,api.post,api.put,api.delete,ApiError, query params, empty responses)components/error-boundary.tsx: React error boundary wrapping the app with a friendly retry UIlib/api/client.tsnow has 30s request timeout, retry on 5xx errors (max 2 retries, exponential backoff)Accept-Languageheader detection, cookie-based locale persistenceBackend (ontokit-api)
test_auth.py— Token validation, role extraction, permission checkertest_config.py— Settings loading, property methodstest_search.py— Search service methodstest_linter.py— Individual lint rulestest_ontology_service.py— RDF graph operations, format conversiontest_projects_routes.py— Project CRUD endpointstest_project_workflow.py— End-to-end: create project → add ontology → create branch → commit → create PR → mergetests/conftest.py: async test client, mock DB/Redis/MinIO, authenticated user fixture, sample data2. Editor Mode System & Theming
Commit:
03e2575Adds a Standard/Developer mode toggle and full theme support, decomposing the monolithic editor page into mode-specific layouts.
lib/stores/editorModeStore.ts) — persistseditorMode("standard" | "developer") andtheme("light" | "dark" | "system") to localStorageuseThemeSync()hook keeps DOMclassattribute in sync; inline<script>in<head>prevents flash of unstyled contentdarkMode: "class"), all components styled withdark:variantsHeadercomponent, available on all pagesapp/projects/[id]/editor/page.tsx, ~850 lines) — owns project loading, auth, branch management, source loading, IRI indexing, entity CRUD, commit flowcomponents/editor/developer/DeveloperEditorLayout.tsx) — tree/source view switching, search, three-panel layoutcomponents/editor/standard/StandardEditorLayout.tsx) — tree + detail panel with inline editingapp/settings/page.tsx) — Editor Preferences section for mode and theme3. Entity Operations & Context Menus
Commits:
4147e40,e722205IRI Generation & Entity Creation
lib/ontology/iriGenerator.ts— generates valid IRIs from labels using ontology namespace, handles collisionsAddEntityDialog— modal for creating Classes, Properties, Individuals with parent selection and IRI previewContext Menus & Toast System
components/ui/context-menu.tsx)canEditcanEdit, styled destructivecomponents/ui/toast-container.tsxrendering floating notifications (bottom-right, auto-dismiss), wired intoapp/providers.tsx4. Form-Based Editing
Commit:
108d93bReplaces raw Turtle source editing with structured, form-based class editing. All saves route through Turtle text manipulation since the backend class endpoint only supports GET.
components/editor/ClassDetailPanel.tsx) — structured editing for:lib/ontology/annotationProperties.ts)lib/ontology/turtleClassUpdater.tsfinds the class block in Turtle text, regenerates it from form data, replaces in source, then saves viaPUT /sourcefindBlockcarefully checks continuation lines (;/,) to avoid matching object references instead of subject definitionsdeprecated,equivalent_iris,disjoint_irisduring form edits5. Auto-Save System
Commit:
9925e96WebProtege-style auto-save with a two-tier draft/commit model.
lib/stores/draftStore.ts) — Zustand + localStorage persist (ontokit-drafts), keyed by"projectId:branch:classIri"lib/hooks/useAutoSave.ts):ClassUpdatePayload, calls the source save endpoint, clears the draftdiscardDraft()— clears draft from store, resets statusonErrorcallback — enables toast notification on failed commitscomponents/editor/AutoSaveStatusBar.tsx) — states: idle (hidden), draft (amber dot), saving (spinner), saved (green check), error (red + retry button); hasrole="status"andaria-liveClassTreeacceptsdraftIris?: Set<string>, renders amber dot indicator on nodes with unsaved changes6. Read-Only Default & Continuous Editing
Commit:
a3bf985cancelledIriRefto prevent auto-re-entryContinuousEditingToggle) — persisted boolean ineditorModeStore7. Shared Tree Renderer & Entity Panels
Commits:
4554022,eb36c2a,1a2a5d3,8b4e347FOLIO-Style Shared Tree Renderer
components/editor/shared/) — shared tree component used by Classes, Properties, and IndividualsEntity Tabs & Detail Panels
8. Suggestion Workflow
Commits:
9e9ee50(web),2cd425c(api)A complete suggester workflow allowing non-editors to propose changes that go through review.
Role System
canEdit: owner | admin | editor | superadmincanSuggest: canEdit || suggesterisSuggestionMode: suggester && !canEdit → routes saves to suggestion APIFrontend (ontokit-web)
lib/api/suggestions.ts) — session CRUD, save, submit, discard, beacon flush, and review methods (listPending, approve, reject, requestChanges, resubmit)resumeSession)isResumedflag for UI differentiationvisibilitychange+beforeunloadflush vianavigator.sendBeaconsaveMode: "commit" | "suggest"+onSuggestSavecallbacklib/api/notifications.ts— unified notification API (list, markAsRead, markAllAsRead)useNotificationshook — 30s polling + event-driven refetchcomponents/layout/notification-bell.tsx) — flat list with type-based icons, unread dots, mark-all-as-readapp/projects/[id]/suggestions/page.tsx) — lists user's suggestions with status, feedback display, resume button, revision badge, GitHub PR linkapp/projects/[id]/suggestions/review/page.tsx) — editors/admins review pending suggestions:RejectSuggestionDialog,RequestChangesDialog?resumeSession={sid}&branch={branch}query params for auto-resumeisResumedBackend (ontokit-api)
ontokit/models/suggestion_session.py) —SuggestionSessionwith statuses: active, submitted, auto-submitted, discarded, merged, rejected, changes-requestedontokit/services/suggestion_service.py, 537 lines) — create session → save commits to dedicated branch → submit as PR → discard with branch cleanupontokit/core/beacon_token.py) — HMAC-signed JWT forsendBeaconauthentication (no session cookie needed)ontokit/worker.py) — auto-submit stale sessions (>30min inactive) via cron job every 10 minutessuggestion_sessionstable with foreign keys to projects and pull_requestsontokit/schemas/suggestion.py) — request/response models for all endpoints9. Drag-and-Drop Class Reparenting
Commit:
3494b1c@dnd-kit/corev6 +@dnd-kit/utilitieslib/hooks/useTreeDragDrop.ts) — manages drag state, cycle detection, Alt-key tracking, auto-expand timer, undo staterdfs:subClassOfoldParentIrisfor rollbackcomponents/editor/shared/DraggableTreeWrapper.tsx) — wraps tree inDndContextwith pointer + keyboard sensorsonAnnouncecallback for drag events viauseAnnounce()10. Graph Visualization
Commits:
c24260a,4ff1731,20f58e7,7318bcd,71e1b06,d217a00Interactive entity relationship graph using React Flow and ELK layout.
@xyflow/reactv12 +elkjs(lazy-loaded vianext/dynamic)lib/graph/buildGraphData.ts) — builds graph fromMap<string, OWLClassDetail>, handles subClassOf / equivalentClass / disjointWith / seeAlso edges, caps children at 20/node, tracks visited set for cycle preventionlib/graph/elkLayout.ts) — layered algorithm, TB/LR direction toggle, 40px node spacing, 80px layer spacinglib/hooks/useGraphData.ts) — fetches focus node + depth-2 neighbors,expandNode()for on-demand expansion,resetGraph(), caps at 100 resolved nodes/ontology/tree/{iri}/ancestorsfor each displayed class to discover the full path to root; adds missing ancestor nodes + subClassOf edges so no node appears floatingowl:Thingis excluded from the graph (no node, no edges); classes whose only parent is Thing are classified as root nodesowl-individualconvention)owl-propertyconvention)components/graph/OntologyNode.tsx) — 7 node type styles (focus, class, root, individual, property, external, unexplored) with dark mode support and type letter badgescomponents/graph/OntologyEdge.tsx) — 4 edge type styles (subClassOf, equivalentClass, disjointWith, seeAlso) with hover labelsGraphLegendinOntologyGraph.tsx) — positioned bottom-right, shows all node type swatches (with badges) and edge type line samples; uses both color and shape/text for accessibilitycomponents/graph/OntologyGraph.tsx) — ReactFlow canvas + MiniMap + Controls + layout direction toolbar + legendRefreshAccessTokenErrorduring graph data fetches11. Keyboard Shortcuts
Commit:
c24260alib/hooks/useKeyboardShortcuts.ts) — singlekeydownlistener with MacCmd/CtrlcompatibilityCtrl+S/Cmd+S— flush current draft to gitCtrl+N/Cmd+N— open Add Entity dialog?— open keyboard shortcuts help dialogEscape— close current overlay/dialogcomponents/editor/KeyboardShortcutDialog.tsx) — groups shortcuts by category with<kbd>badgesCtrl+Khint displayed near search icon in toolbarformatShortcut()andisMac()exported for consistent display12. Accessibility
Commit:
c24260aapp/layout.tsxwith.skip-linkCSS classcomponents/ui/ScreenReaderAnnouncer.tsx) —useAnnounce()hook with polite + assertive ARIA live regions; mounted inapp/providers.tsxAutoSaveStatusBarhasrole="status"+aria-liveon all status variantsuseTreeDragDrophasonAnnouncecallback; both layouts passannouncefromuseAnnounce()aria-activedescendanton tree container, uniqueidon each tree itemaria-labelinstead oftitleAnnotationRowandInlineAnnotationAdderinputs havearia-label@media (prefers-reduced-motion: reduce)disables all animations@media (prefers-contrast: more)increases focus ring width13. UI Polish & Bug Fixes
Commits:
1e38288,15d9cfa,20f58e7,bcbfaaf,4ff1731,9a379b5divs ensure consistent row height when a language flag is unavailableClassDetailPanelheader row to fix overlap with the "Edit" buttonRefreshAccessTokenErrorand redirects to sign-in instead of showing cryptic errors; added to graph data fetchesrdfs:seeAlsorelationships in graph visualization14. Backend — API Infrastructure
Commit:
1cab6c0Comprehensive backend improvements covering missing features, code quality, and test coverage.
Lifespan Handlers (
ontokit/main.py)Authentication & Authorization (
ontokit/core/auth.py)urn:zitadel:iam:org:project:rolesclaimTokenPayloadandCurrentUserSearch Service (
ontokit/services/search.py)tsvector/tsquery*query returns all entities of requested type)Rate Limiting
slowapidependency with per-IP limits (100 req/min default)Middleware (
ontokit/core/middleware.py)X-Request-IDheader)X-Content-Type-Options,X-Frame-Options,X-XSS-Protection,Strict-Transport-Security,Content-Security-PolicyCode Quality
ontokit/core/exceptions.py) —NotFoundError,ValidationError,ConflictError,ForbiddenErrorwith global handlers15. Backend — Suggestion Sessions
Commit:
2cd425cFull backend implementation of the suggestion workflow (see Section 8 for the frontend counterpart).
SuggestionSessiontable with session lifecycle trackingnavigator.sendBeacon(tab close/hide)suggestion_sessionstable with proper FKs and constraints16. Backend — Bug Fixes & Role Changes
Commits:
3fe55df,eb8e306HttpUrlis not a plain string; wrappedcls.iri.lower()instr()for class tree sort keys in bothget_root_classesandget_class_childrenMemberBaseschema default changed from"viewer"to"suggester"; join request approval assigns"suggester"instead of"editor"Stats Summary
New Dependencies (ontokit-web)
@dnd-kit/core,@dnd-kit/utilities— drag-and-drop@xyflow/react,elkjs— graph visualizationvitest,@vitejs/plugin-react,happy-dom— testingzod— environment validationKey Architectural Decisions
PUT /sourcebecause the backend class endpoint only supports GET. This enables form-based editing without backend schema changes.EntityTree/EntityTreeNodecomponents reused across Classes, Properties, and Individuals tabs, reducing duplication and ensuring consistent behavior.elkjsand@xyflow/reactloaded vianext/dynamicto avoid bundling heavy visualization libraries for users who don't need them.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes & Improvements
CI & Config
Tests