feat(tracker): Gantt — #3b dependencies + cascade + critical path#10855
Draft
MichaelUray wants to merge 143 commits into
Draft
feat(tracker): Gantt — #3b dependencies + cascade + critical path#10855MichaelUray wants to merge 143 commits into
MichaelUray wants to merge 143 commits into
Conversation
Schema-only foundation for the upcoming Gantt-chart view in tracker. No UI in this PR. Changes: - Issue.startDate: Timestamp | null (interface + IssueDraft + @prop with @Index) - Milestone.startDate: Timestamp | null (interface + @prop, reusing the existing tracker.string.StartDate IntlString) - New DependencyKind type ('finish-to-start' | 'start-to-start' | 'finish-to-finish' | 'start-to-finish') - New IssueRelation AttachedDoc class with kind: DependencyKind, signed lag: number — registered in models/tracker via TIssueRelation - 7 new IntlString keys: IssueStartDate, GanttDependency, GanttDependency{FinishToStart,StartToStart,FinishToFinish,StartToFinish}, GanttLag — all 13 locales updated - Cross-plugin literal updates in importer + github sync to satisfy the new required Issue.startDate / Milestone.startDate fields: - packages/importer/src/importer/importer.ts: AttachedData<Issue> literal - services/github/pod-github/src/sync/issueBase.ts: 'startDate' added to GithubIssueData Omit list (github sync does not own scheduling) - services/github/pod-github/src/sync/issues.ts + pullrequests.ts: AttachedData<Issue|GithubPullRequest> literals Out of scope (deferred to follow-up PRs): - UI for Gantt view, drag/resize, dependency editor, critical path - blockedBy → IssueRelation migration (ships atomically with the writer redirect in the dependency-UI PR) - LinkIssues permission (tracker uses forbid-style permissions; needs maintainer discussion) - Activity-feed wiring for IssueRelation (needs a producer to test against) - IssueTemplate.startDate (template propagation semantics undecided) Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
3 tests covering migrateAddStartDate: - writes startDate=null to Issues in DOMAIN_TASK with the right filter - writes startDate=null to Milestones in DOMAIN_TRACKER with the right filter - issues exactly two update calls (one per class) Follows the MigrationClient mock pattern from models/chat/src/__tests__/migration.test.ts. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…tion Backfills startDate=null on existing Issues (DOMAIN_TASK) and Milestones (DOMAIN_TRACKER) so the new schema field has a defined value on every pre-existing document. Idempotent via the standard tryMigrate state-key mechanism (state: 'gantt-add-startdate'). Verified domain choices against existing migration helpers: - migrateIdentifiers / passIdentifierToParentInfo use DOMAIN_TASK for Issues (lines 145, 161 in this file). - TMilestone @model decorator confirms DOMAIN_TRACKER for Milestones (models/tracker/src/types.ts:372). Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ghten typing UI changes (so the new schema fields are actually editable, in chronological order Start → Due/Target): - New StartDateEditor.svelte (mirrors DueDateEditor.svelte for startDate) - ControlPanel: render Start Date row above Due Date row in the issue side panel; both always-visible (no `!== null` guard) so users can set them on issues that don't have a date yet - NewMilestone form: Start Date input above Target Date input - Milestone list view: Start Date column before Target Date column - TIssueRelation: tighten interface to `extends AttachedDoc<Issue, 'relations'>` so attachedTo + collection are statically typed. The model class re-declares `collection: 'relations'` to match the narrower base. - Drop 4 unused Dependency-kind IntlString keys (FinishToFinish, FinishToStart, StartToFinish, StartToStart) — they had no consumer in PR 1; will be re-introduced in PR 4 (dependency editor). - Simplify migration.ts comments — drop ageing line-references. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The DocAttributeBar side panel sorts attributes by attr.rank ?? toRank(_id) (see plugins/view-resources/src/components/ClassAttributeBar.svelte:42-47), so without explicit ranks the visible order on a Milestone was hash-based (startDate before Status, breaking the chronological flow the user expects). Set ranks so the side panel renders Status → Start date → Target date. Comments and attachments stay where they are (they're collections, filtered out of the attribute panel by categorizeFields). Issues are unaffected — the Issue side panel is the custom ControlPanel.svelte which renders Start date / Due date in explicit slots (see PR 1's UI commit). Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…body in chronological order
The right-side DocAttributeBar sorts attributes by attr.rank ?? toRank(_id),
giving startDate before status (toRank('startDate') < toRank('status')
lexicographically). Setting an explicit rank via @prop's third arg did not
propagate through the workspace upgrade for existing Attribute documents
in the model TX log — the rank made it into the bundled txes but the
existing Attribute creation TXes are not replaced on upgrade-workspace.
Pivot: render Status, Start date, Target date in the EditMilestone body
in explicit chronological order, and add 'status', 'startDate', 'targetDate'
to ignoreKeys so they don't appear duplicated in the side panel. This
mirrors how Issue's ControlPanel.svelte handles its date fields.
Reverts the no-op @prop rank attempt.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…aces The fulltext-pod's compiled model version (baked into bundle/model.json via common/scripts/version.txt at build time) lags whenever the workspaces have been migrated to a newer patch but the pod was not rebuilt. In that state the indexer rejects every incoming Tx with a `wrong version` warning, new issues silently fail to land in Elasticsearch, and search returns empty results for any document created after the migration. Bumping `version.txt` aligns the compiled model with the workspaces. All future builds (front, transactor, workspace, tool, fulltext) will emit 0.7.423, the indexer accepts the Tx stream again, and the deferred backlog gets consumed automatically — no manual reindex needed. This commit is the build-side companion to the schema migration in this same PR. Without it the fulltext-pod cannot consume the migrated workspace's Tx events. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…reactive queries PR 2 read-only Gantt is now end-to-end functional: - Two reactive queries (issues, milestones) - buildLayout(issues) -> rows with depth + isSummary - createTimeScale(zoom, dateRange.from) -> date<->px math - Summary ranges computed per parent issue from children's date span - Local zoom state (Day/Week/Month/Quarter) - no ViewletPreference plumbing - Horizontal scroll via canvas-scroller; vertical scroll moves Sidebar via translate Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- viewlets.ts: register IssueGantt AFTER IssueKanban so List stays default - viewlets.ts: drop showColorsViewOption (canvas does not honour it yet) - GanttBar.svelte: normalise reversed startDate>dueDate ranges UX: - GanttSidebar adds Title column with sticky two-column header - Sticky time-scale header decoupled from milestone strip (layout fix) - Per-row jump-to-bar arrow (Plane-style) when bar is offscreen - ResizeObserver initialises viewport on mount and on resize Milestones as Gantt rows + collapse: - Milestones group their issues as nested children (depth+1) - Collapsible toggle per parent row, local collapsedIds Set - Milestone summary bar uses existing GanttBar with aggregated range Icon: - Register tracker.icon.Gantt to #timeline svg (Gantt pictogram) Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- Visible top row with [«] [Today] [»] | Day Week Month Quarter | ⚙ - Today scrolls to current date, prev/next page-scroll by 80% viewport - Settings popover toggles Issue-Code and Title columns Layout: - Bigger row height (28→36) and bar font (11→13px) for readability - Sidebar reworked into flex-column with separate clipped rows region so scrolled rows can never paint over the sticky header(layout fix) - Drag handle between sidebar and canvas (120–600px range) - ResizeObserver re-syncs viewport on drag/resize/zoom changes Interaction: - Vertical gridlines aligned to time-scale ticks (Plane-style) - Row hover highlights both sidebar and canvas, with rich HTML tooltip (issue title + start + due + duration) - Title click in sidebar dispatches openIssue → showPanel(EditIssue) - Double-click on canvas bar dispatches openIssue - Wheel forwarding from sidebar to canvas-scroller (vertical scroll works while hovering issue list) - Pointerdown + drag on empty canvas pans both axes - Jump-to-bar buttons now use the bar's true left-edge target Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
totalCanvasWidth (was viewport width), with viewBox in scroll-content coordinates. The sticky header lives in the same coordinate system as the canvas-stack so horizontal scroll no longer clips the time-axis. - showPanel uses tracker.component.EditIssue instead of the hardcoded string + 'as any' cast. - Removed unused GanttToolbar.svelte and GanttMilestoneFlag.svelte (dead files since toolbar moved into GanttView and milestone flags became rows). Toolbar: - Time-navigation cluster: ⏮ « Today » ⏭ + native date picker that jumps to a specific date. Replaces the lone Today button. Interaction polish: - Sidebar wheel forwarding now uses direct scrollTop/scrollLeft mutation with deltaMode scaling — same speed as native canvas scroll. - TodayMarker no longer hides when scrolled offscreen (SVG width handles clipping); milestone reference lines render unconditionally for the same reason. Tests: 25/25 jest pass; svelte-check 0 errors. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- Register two ToggleViewOptions on the Gantt viewlet: ganttShowIssueCode (default OFF) and ganttShowTitle (default ON). These show up in the standard Customize-View dropdown. - Drop the per-component settings popover from GanttView; sidebar column visibility now reads viewOptions directly. - Hover tooltip always surfaces the issue code (e.g. OSTRO-31), even when the issue-code column is hidden. - IntlStrings + en/de/es/fr/it/ja/pt/ru/zh/cs translations added. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Bug: .gantt-sidebar rendered at its natural height (≈ rows × 36px), and .sidebar-host had overflow:visible, so for many issues the sidebar visually overflowed gantt-body which in turn made gantt-body's scrollHeight balloon to the sidebar's natural height. The result was that scrolling over the sidebar appeared to scroll the page area instead of just the canvas. Fix: - .sidebar-host now overflow:hidden + height:100% + flex-column - .gantt-sidebar height:100% + flex:1 1 auto so it fills the host rather than stretching past it Verified: gantt-body scrollHeight == clientHeight, only .canvas-scroller has scrollable content. Wheel-forward over the sidebar moves canvas-scroller and the sidebar transform together. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ticky) Replaces the dual-scroller architecture (sidebar + canvas as siblings, manual wheel-forwarding for the sidebar) with a single .gantt-scroller that wraps both columns in a CSS grid. Sidebar uses position:sticky left:0 so it stays at the left edge during horizontal scroll, while the time-axis header uses position:sticky top:0. Browser handles all wheel events natively at native speed. Why: the previous design rendered the sidebar at its natural height (rows × 36px), which could be much larger than the visible viewport. overflow:visible on the sidebar host let it bleed past gantt-body and made the page area appear to scroll. The wheel-forward hack only worked for synthetic events; in real browsers, wheel events over the sidebar either scrolled too slowly (deltaMode mismatch) or stopped firing. Layout (CSS grid, 2 rows × 3 cols): - (1,1) sticky top+left: corner with column titles - (1,2) sticky top: resize-corner (5px gutter) - (1,3) sticky top: time-axis header - (2,1) sticky left:0: GanttSidebar rows - (2,2) sticky left:Wpx: vertical resize-handle - (2,3) normal: GanttCanvas SVG overflow:scroll on .gantt-scroller forces both scrollbars to always render — the user explicitly asked for visible scrollbars (Plane parity). GanttSidebar drops its scrollTop prop and the transform-based row positioning; rows now flow naturally inside the scroll container. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The standard NewIssueHeader is rendered by the Tracker shell outside the viewlet. Inside the Gantt view this looked like the action was missing. Add a prominent + New issue button in the gantt-toolbar that opens CreateIssue with the current project space pre-selected, matching the behaviour of NewIssueHeader. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
- Remove "+ New issue" button from GanttView. PR2 stays read-only; issue creation lives in PR3 with edit/drag. - Localize all visible Gantt strings via use:tooltip + Label, no more hardcoded English: Today, Jump to start/end, Previous/Next period, Jump to date, Issue, Title, Milestone, Start/Target/Due, Expand, Collapse, Scroll left/right to bar. - Add 9 new IntlStrings (GanttToday, GanttJumpToStart, GanttJumpToEnd, GanttJumpToDate, GanttPreviousPeriod, GanttNextPeriod, GanttScrollLeftToBar, GanttScrollRightToBar, GanttExpand, GanttCollapse) on top of the existing GanttShowIssueCode and GanttShowTitle. - Restore all 13 locale files to their PR1 baseline formatting and add only the additive Gantt entries (~17 new lines per locale, English fallback). ko.json, pt-br.json, tr.json now ship the same Gantt keys as the rest. Tests: tracker-resources svelte-check 0 errors; tracker-assets locale test pass. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
User-requested UX consolidation: the same compact "+ New issue" button should appear above every Tracker viewlet (List, Kanban, Gantt) instead of the bulky text+dropdown HeaderButton. - Replace HeaderButton with two stacked Button instances: * primary blue circular icon-only IconAdd (newIssue / newProject) * regular grey circular IconDropdown with secondary actions popup - Always visible regardless of viewlet (kind of a tab-bar shortcut) - showTooltip surfaces the keybinding from the existing tracker action - Draft indicator stays as small dot on top-right of the plus button Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
the shifts.size === 0 early-return, so a parent-drag of an editable parent with a non-editable child and no external successors would silently commit the child's date too. Move the permission check above the no-cascade exit. New test 18 asserts the bug: Parent + locked Child as primaryEdits, zero relations -> result must be permission-denied, not no-cascade. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
1. CP algorithm: single project finish . Drop per-component
approach. Standard CPM: one project = max(EF) across all sinks.
Isolated issues with earlier EF get positive slack, not 0. Test
'two unrelated issues' updated to assert this.
2. UI wiring — edits to existing
files instead of creating GanttToolbar that doesn't exist:
- GanttView reads ganttCriticalPath / ganttSlackColumn from
viewOptions read-only; user toggles via Customize-view panel.
- GanttCanvas forwards criticalSet/criticalRelations/
violatedRelations/cpSlack/showCriticalPath.
- GanttBar uses real vars (x, barY, w, barH). Critical bars get
red border + 18%-opacity red overlay. Slack glyph (light-grey
trailing rect) on non-critical bars when CP on.
- GanttDependencyArrow: critical=solid-red, violated=red-dashed.
- GanttSidebar adds slack-column cell with 'Nd' / 'CP' badge.
3. CP recompute debounced 200ms; cycle banner throttled 60s.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
PR6 'polish' wrap-up:
1. PNG export (lib/exporter.ts):
- exportGanttSvgToPng(svg, opts) serializes the SVG with inline
computed styles, rasterises through <img src=data:> and a
canvas at devicePixelRatio scale, returns a PNG Blob.
- downloadBlob() triggers a browser download.
- GanttView wires a keyboard shortcut 'E' / 'e' to capture the
visible .gantt-canvas SVG and save as 'gantt-<YYYY-MM-DD>.png'.
2. Keyboard shortcuts (GanttView onKey):
- +/= zoom in (cycle day -> week -> month -> quarter)
- -/_ zoom out
- ? opens GanttHelpPopup with the full shortcut list
- E exports to PNG
- All shortcuts are scoped to the Gantt root via the existing
containerEl.contains(document.activeElement) gate so global
bindings aren't hijacked.
3. GanttHelpPopup.svelte: modal listing all 8 shortcuts (drag, zoom,
help, export, Esc, Alt-bypass). Esc or ? dismisses.
4. German translations:
- Replaced the 13 English placeholder values in de.json for
GanttShow*/GanttToday/GanttJumpTo*/GanttScroll*/GanttExpand/
GanttCollapse with proper translations.
- Added 49 new DE keys covering PR3 drag/confirm, PR4a dependencies,
PR4b cascade simulation + popup, PR5 critical path + slack, PR6
export + help.
4 new i18n keys in plugin.ts (GanttHelpTitle/GanttHelpEsc/GanttExport/
GanttExportFailed) + matching EN values. svelte-check 0 errors.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
1. GanttView.onDestroy clears the pending cpDirtyTimer:
- PR5 schedules the critical-path recompute via setTimeout with a
200 ms debounce. If the view unmounts while a recompute is queued,
the timer fires after the reactive store handles are torn down —
dangling write + potential late banner. clearTimeout on destroy
fixes both.
2. critical-path topoSort in-degree guard:
- inDegree was incremented whenever the relation's target is in the
scheduled set, regardless of the source. If a source ref isn't in
the set the target's in-degree would stay positive forever (the
source is never dequeued) and the target would silently drop from
the topological order. The caller passes activeRels (filtered to
both-endpoints-scheduled), so the bug is mitigated in practice,
but the helper now self-guards: only increment when both endpoints
are scheduled. Same change applied to plan §Task 3 topoSort body.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
A bar with isViolated=true but isCritical=false was previously only getting the dashed stroke-dasharray; the stroke colour stayed at the data-conflict signal and should be visually as loud as critical bars. Treat violated like critical for stroke colour and width; keep the 4 2 dasharray as the disambiguator between the two states. The violated CSS class is now also applied for downstream rule hooks. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…cator
Two related UX bugs reported by user:
1. Blue connector dot occluded by dependency arrow
- GanttDependencyArrow renders an invisible 12 px stroke as a click-
target (line 77). When that target overlapped the 10 px connector
hit-circle, SVG paint order let the arrow win every click and the
dot became un-grabbable.
- Fix: extract the connector dot from GanttBar (rendered inside the
bars layer, before the dep-layer) into a new sibling overlay group
in GanttCanvas, rendered AFTER GanttDependencyLayer. Paint order
now puts the dot above all arrows without changing the dep-arrow
click-target geometry.
2. No drop-target indicator on the receiving bar during connector-drag
- During connector-drawing the user only saw the bezier preview
dangling from the source; the target bar gave no visual cue.
- Fix: while activeDrag is connector-drawing OR connector-target-
hover, the overlay renders a small grey 'drop here' circle at the
FS target anchor (left edge) of every editable bar that isn't the
source. When the cursor is over that bar (connector-target-hover),
the circle grows to 6 px and switches to the same indigo as the
source dot — a strong 'release now' affordance.
Both behaviours land in the same overlay <g> for paint-order coherence.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…resenter
User feedback (2026-05-13): the Dependencies panel was packing kind +
direction + identifier + truncated title into one tight cell, hard to
scan and not editable without going back to the Gantt. Activity log
entries for new dependencies just said 'New related to: Dependency'
which gave no clue what was added or removed.
Two changes:
1. IssueDependenciesPanel rewrite (still in the issue side panel):
- Split into Predecessors (incoming) + Successors (outgoing) groups
with small section labels
- Coloured kind badge (FS=indigo / SS=violet / FF=amber / SF=red)
matching the same palette used in cascade-popup legend
- Separate lag pill (skipped when lag=0)
- Full title visible (with overflow-ellipsis + native title tooltip
for the rare wide-screen cases)
- '+' button in the section header opens ObjectSearchPopup to pick
a target issue, writes a default FS lag=0 IssueRelation. User can
click the row immediately to refine kind/lag via DependencyEditor.
2. IssueRelationPresenter:
- Tiny single-line component '<kind> [<lag>] → <ident> <title>'
- Registered as the ObjectPresenter mixin for tracker.class.
IssueRelation in models/tracker/src/presenters.ts
- Activity entries now render as 'New related to: FS +2d → OSTRO-29
Office/social container' instead of the generic 'Dependency'.
Same applies to remove-activity entries — the user can see
exactly which dependency disappeared.
Adds two new i18n keys (GanttSuccessors) + DE translations.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…t path
the 30 ms lastDownAt debounce was masking:
1. GanttConnectorDot template <g> + <circle.gantt-connector-dot> +
<circle.gantt-connector-hit> all bound on:pointerdown + on:mousedown
2. GanttConnectorDot onMount addEventListener('pointerdown'/'mousedown')
directly on the hit-circle DOM node
3. GanttView document-capture pointerdown/mousedown listener
(handleNativeConnectorDown) scanning .closest('.gantt-connector'),
plus a synthetic 'gantt-connector-start' CustomEvent dispatched from
onDown and caught by handleConnectorStartEvent on document
Paths 2 and 3 were workarounds from the bezier-preview-debug era —
when Svelte events weren't firing reliably for synthetic Playwright
mousedowns. The real bug then was a reactivity issue (livePath function
indirection) which has been fixed since. Now that the connector dot is
also rendered in the canvas-overlay where Svelte event flow is well-
behaved, the workarounds are redundant.
After this commit only one mechanism remains:
GanttConnectorDot <circle.gantt-connector-hit> on:mousedown=onDown
→ dispatch('connectorDown', { cursorX, cursorY })
→ GanttCanvas on:connectorDown forwarder
→ GanttView on:connectorDown handler
→ reduce(state, mousedown-connector) + attachWindowDragListeners()
Removed:
- GanttConnectorDot.onMount + onDestroy addEventListener pair
- GanttConnectorDot dispatch of 'gantt-connector-start' synthetic event
- GanttConnectorDot template bindings on the wrapper <g> and the
visible <.gantt-connector-dot> (the visible dot is pointer-events:
none anyway; the wrapper was redundant with the hit-circle binding)
- GanttView handleNativeConnectorDown function
- GanttView handleConnectorStartEvent function
- GanttView containerEl + document addEventListener for both above
- gantt-root <div> capture-phase on:pointerdown / on:mousedown attrs
The lastDownAt debounce is kept as a defensive guard against the rare
browser-double-fire of mousedown on the same hit-circle; it's now a
no-op in normal flow.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
introduced at the GanttCanvas level — canvasEl.addEventListener for
pointerdown + mousedown delegating to startConnectorFromIssueId via
.closest('.gantt-connector'). This violated the 'single event path'
commit message and worse, GanttConnectorDot only listens to mousedown
while this delegate listened to both, so a single physical click on
the dot would fire pointerdown via the canvas path AND mousedown via
the Svelte path with no debounce shared between them.
Removed everything redundant from GanttCanvas.svelte:
- canvasEl let + bind:this={canvasEl} on the <svg>
- forwardConnectorDown (was attached to the now-removed in-bar dot)
- startConnectorFromIssueId
- onCanvasConnectorDown
- onMount / onDestroy hooks that wired the listeners
- on:connectorDown={forwardConnectorDown} on the bar-render <GanttBar>
- onMount + onDestroy imports (no remaining uses in this file)
Surviving path is exactly what the v85 commit message described:
GanttConnectorDot <circle.gantt-connector-hit> on:mousedown=onDown
→ dispatch('connectorDown', { cursorX, cursorY }) // Svelte
→ overlay <GanttConnectorDot on:connectorDown={…}> inline handler
in GanttCanvas converts to { source, originPx } and dispatches
→ GanttView on:connectorDown={handleConnectorDown}
→ reduce(state, mousedown-connector) + attachWindowDragListeners
Net diff: -45 lines, including imports.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…ab, intra-project, remove button User-reported issues with the issue-editor Dependencies panel: 1. 'Search for Task' list empty after first + click, then Relations row disappears entirely. Root cause: ObjectSearchPopup's ignore prop becomes $nin: <Ref[]> in the DocumentQuery — the CockroachDB adapter crashes on JSONB $nin with 'unsupported comparison operator: _id != ALL …'. The thrown query also tears down the live-query for the panel, so the panel itself disappears once the error surfaces. Fix: drop the ignore param entirely. Filter self/duplicate/cross- project in the result callback instead. 2. + click opens the popup without focusing the Issues tab. Fix: pass allowCategory: [tracker.completion.IssueCategory] so only the Issues tab is shown — eliminates the wrong-default-tab problem and removes irrelevant tabs (Person, Project, Document) for a dependency picker. 3. Picker shows cross-project issues. Phase-1 only supports intra- project dependencies. Fix: when the result space != current issue's space, reject with a notification banner instead of writing the relation. Cycle check (PR4a wouldCreateCycle) also applied to the callback path so the issue-editor flow has the same protections as the Gantt connector drag. 4. No way to remove a dependency from the panel. Fix: per-row hover-only '×' button that calls ops.removeDoc on the IssueRelation. Same permission gate as the editor: source-side editable user only. 5. Identifier displayed prominently, title secondary. Fix: swap — title primary, identifier moved into the native title tooltip. Same swap applied to IssueRelationPresenter so activity entries read 'New related to: FS +2d → Storage silo construction' (instead of '… → OSTRO-29 Storage silo'). The OSTRO-29 identifier stays accessible via hover tooltip on the activity row. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Replace ObjectSearchPopup with ObjectPopup using docQuery:
{ space: issue.space } so cross-project issues no longer appear
in the dependency picker — matches the SetParent picker
convention (Phase-1 deps are intra-project only).
ignoreObjects excludes self and existing successors before the
list is rendered; cycle detection stays in the callback because
it needs the full relation graph.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Bare ObjectPopup with no slot="item" loads docs but renders
nothing — its default item fragment is gated on
{#if $$slots.item}. v88 invoked ObjectPopup directly via
showPopup() which can't pass slots, so the dependency picker
showed an empty list.
Add SelectDependencyIssuePopup wrapper that mirrors
SetParentIssueActionPopup's structure: ObjectPopup with the
required slot="item" (status icon + identifier + title),
plus docQuery: { space: issue.space } and ignoreObjects to
keep the picker project-scoped and excluding self/existing
successors.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The 'Link existing sub-issue' affordance already existed as an icon-only ghost Button next to QueryIssuesList's prominent '+ Add sub-issue'. It was undiscoverable: only a hover-tooltip revealed what it did. Add an inline label so the action reads visibly next to its icon, and provide a German translation for the i18n key. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
UX: replace direct + buttons for parent/sub-issue hierarchy with a two-option chooser popup (HierarchyAddPopup) — both directions now visibly offer 'Create new' AND 'Link existing' regardless of whether the issue already has children. Plumbing: - HierarchyAddPopup (new) — chooser with 'parent' | 'sub' direction - CreateIssue dispatches close-with-id on success so the new 'Create new parent' flow can write attachedTo on the calling issue - QueryIssuesList accepts customAddAction prop; slot=buttons ungated so any caller-supplied accessory buttons stay visible without sub-issues - SubIssues passes customAddAction that opens the chooser; legacy icon-only LinkSubIssue button removed (chooser supersedes it) - EditIssue Set-parent button opens chooser; routes 'create' to CreateIssue and on close re-ranks current issue under new parent Resizable pickers: - new ObjectPopup/DocPopup width: 'resizable' value - popups.scss .width-resizable: 48rem × 36rem default, resize: both, min 28rem×16rem, max 96vw×96vh - SelectDependencyIssuePopup, SetParentIssueActionPopup, LinkSubIssueActionPopup default to 'resizable' so the existing call sites pick up the larger draggable pickers automatically i18n: AddParentIssue, AddSubIssue, CreateNewParentIssue, CreateNewSubIssue, LinkExistingParentIssue in en + de. Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
The Dependencies-panel "+" button silently wrote outgoing (successor) relations only. Add a two-option chooser mirroring the hierarchy add chooser pattern: "Add predecessor" / "Add successor" with directional hint tooltips. Predecessor flow attaches the new IssueRelation to the picked issue (picked is the source), permission-gates on the picked issue, and the picker's ignore set uses incoming.attachedTo refs rather than outgoing.target refs. extraIgnore prop added to SelectDependencyIssuePopup so callers don't have to wrap raw ids in fake IssueRelation shapes. i18n: AddDependency, AddPredecessor, AddSuccessor + direction hints (en + de). Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
…view comments
The dependency-add cycle check in the issue-editor panel used only
incoming + outgoing slices of the current issue:
wouldCreateCycle(sourceId, targetId, [...incoming, ...outgoing])
That catches direct A→A and direct duplicates but misses transitive
cycles. Example: with C→B and B→A already in place, adding A→C from
A's panel passes the check because C→B is not in A's local slice —
the DFS never sees the back-edge B→A→C from the C side, so the new
edge silently closes the loop. The Gantt view already does a
project-wide check; the issue panel must match.
Add a third live-query bound to { space: issue.space } that loads all
relations in the project, and pass the full list to wouldCreateCycle
on add. The visible Predecessors/Successors lists keep using the
existing incoming/outgoing slices.
gantt + dependency code. The substance of each note is preserved but
rephrased so the comments document the technical reason, not the
review thread that surfaced it.
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
Signed-off-by: Michael Uray <michaeluray@users.noreply.github.com>
This was referenced May 18, 2026
Draft
|
Connected to Huly®: UBERF-16446 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Brings dependencies, cascade-scheduling, and critical path to the Gantt. Issues can be linked through typed
IssueRelationMixins (FS / SS / FF / SF), the canvas paints arrow connectors between dependent bars, dragging a bar with downstream dependencies opens a "cascade confirm" popup that previews and atomically commits the shift across the whole dependency tree, and an optional critical-path mode highlights the longest dependency chain plus the per-issue slack.This is the largest single PR in the stack (~6 000 lines, 61 files). Most of that is the dependency simulation library + its test suite, plus the cascade-confirm UI.
What's in this PR (+79 incremental commits)
Dependency editing (PR4b family)
feat(tracker)predecessor / successor direction chooserfeat(tracker)readable Dependencies panel +IssueRelationactivity presenterfeat(tracker)unified hierarchy add chooser + resizable pickersfix(tracker)scoped dependency picker to current project; wrapObjectPopupso items renderfix(tracker-resources)confirm dialog before dependency removalCascade scheduling (PR4b)
simulateCascadelibrary — FS push + no-cascade, SS/FF/SF anchor model, pull-predecessor support, cycle bailoutcommitWithCascade— body-drag wiring + single-confirm flowConfirmCascadePopupwith mini-timeline previewCritical path (PR5)
ganttCriticalPath+ganttSlackColumntogglesMisc
feat(tracker-resources)show GanttIssueRelations in the issue editorfix(notification-resources)returnArrayBuffer-backed push keyStack overview
…pr1-schema…pr2-readonly…pr3a-edit…pr3b-deps-cascade-cp…pr4-polish…pr5-tier2…pr6-tier3-virtualization…pr7-tier4MichaelUray:feat/gantt-sectionMarked as draft to signal stack dependency on PR1+PR2+PR3a.
How to review
→ Fork compare: PR3a...PR3b — 79 commits, 61 files
Recommended reading order:
simulateCascadeand its tests inplugins/tracker-resources/src/components/gantt/lib/commitWithCascadewire-up in the drag controllerConfirmCascadePopup.sveltefor the UI surfaceTesting
DCO
All commits are
Signed-off-by.