mirror of
https://github.com/windmill-labs/windmill.git
synced 2026-09-09 00:04:10 +00:00
* feat(flow-editor): measure the redesigned step panels Instruments the flow editor's step, loop and branch panels on the existing anonymous `feature_usage` channel, so the redesign can be judged on how the panels are actually used rather than on nothing. Eight event kinds under a new `flow_editor` feature: panel opens and their dwell (bucketed, per placement), placement-preference overrides, which settings get configured or cleared, settings that read as invalid, the prop-picker connect lifecycle, AI input suggestions, and the step header menu that "Save to workspace" now lives behind. Settings changes are diffed off `describeStepSettings`, the same view the graph badges render, so the telemetry vocabulary cannot drift from the one on screen. Only `panel_open` and `setting` carry an entity id — one opaque id per editor mount — since a per-entity row is only worth its cost where the spread per editing session is the question. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow-editor): keep the panel telemetry honest Review follow-ups on the instrumentation: - The top dwell bucket was `120s+`, and `+` is outside the charset `is_identifier_shaped` accepts, so `log_feature_usage` skipped those events and still answered 204 — the longest visits vanished with no error on either side. Renamed to `120s_plus` and pinned every emittable key against the backend's charset in a test, since the producer is TypeScript and the validator is Rust. - Dropped the per-session entity id from `setting`: it would pay a row per session per day across twenty-four keys, for a distribution its plain counter already largely answers. - An armed connect that went away with its component never reported, so `open` did not balance against `insert` + `abandon`. - Session preview tabs keep hidden editors mounted, which billed panel time nobody spent. `FlowEditorView` now publishes the visibility it already knows about. - Re-picking the active placement row logged a move, which also made `auto:from_docked` mean two different things. - The last dwell of a session was lost on tab close, since Svelte tears components down on navigation but not on `pagehide`. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * refactor(flow-editor): narrow the telemetry to panel placement The eight-kind instrumentation measured more than could be read. With nothing recorded before the redesign there is no baseline to compare panel opens, dwell times, settings usage or connect funnels against, so those counters answered questions nobody could act on while costing a row per key per day in an instance-wide table. What remains are the three numbers the modal panel is actually judged on: how often the 1280px breakpoint puts the panel in a modal, and how often people override that in each direction. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow-editor): stop counting placement in session preview tabs Preview tabs keep every flow editor mounted and laid out at panel width whether or not it is the visible one, and that panel is narrower than the breakpoint by construction. Each flow tab opened in a session therefore emitted a `breakpoint_modal` on mount, and one drag of the session panel across 1280px emitted one per mounted tab — with no host dimension in the key to separate that from the crossings the counter exists to measure. Also corrects the comment on the no-op placement guard, which justified itself with a key vocabulary that no longer exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow-editor): make the three placement counters comparable Sessions were excluded from the breakpoint counter but not from the two override counters, so a pin made in a session landed in the same bucket used to judge the breakpoint, with no crossing in the denominator to read it against. All three are now gated together. An override is also only counted when it moves the panel. Choosing "Detached" on an editor the width had already put in a modal states a preference without changing anything, and the aggregate carries no width to separate that from the wide-screen override that is the actual signal. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(flow-editor): describe the two override keys by what emits them They documented themselves as overriding `auto`, which is no longer the rule: pinning Attached on a wide editor overrides `auto` and emits nothing, while going from an Attached pin to Detached below the breakpoint emits `force_detach` even though `auto` would have produced a modal there too. This file is what someone reads when interpreting the numbers, and "override of auto" is the misreading the emission rule exists to prevent. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow-editor): count the panel moving, not the breakpoint being armed The tracker held "the breakpoint is responsible for this modal" rather than "the panel is modal", so on a narrow editor pinning Detached and releasing it back to Auto emitted a second breakpoint_modal for a panel that never moved. It also died with the editor, which FlowBuilder rebuilds through a `{#key}` on every reload — each rebuild re-armed it and counted the same narrow editor again. Both inflate the denominator that the two override counters are read against, and both bias it the same way: toward concluding that nobody overrides the breakpoint. The tracker now follows the panel's placement across preference changes, and FlowBuilder owns it from above the `{#key}`, which also puts the session exclusion in one place instead of at each call site. The moves-only rule moves into `forcedPlacementEvent` so both halves of it sit in the module the tests can reach. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(flow-editor): ignore placements measured before the editor is laid out A reload rebuilds the editor through `{#key renderCount}`, and the panel controller is rebuilt with it: its width restarts at zero, which resolves to `docked` because that is what is safe to render rather than because the editor is wide. The breakpoint tracker read that transient as the panel having docked and counted the real width landing as a fresh crossing, inflating the denominator both override ratios are read against. `useFlowPanelMode` now exposes `measured`, and the tracker skips anything unmeasured instead of recording it as a placement. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * docs(flow-editor): state the placement invariants once each The width-zero rule had accumulated at four sites, two of which forward it without being able to break it. Keep it beside the guards that enforce it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
95 lines
3.8 KiB
TypeScript
95 lines
3.8 KiB
TypeScript
import { getContext, setContext } from 'svelte'
|
|
import { logFeatureUsage } from '$lib/utils/featureUsage'
|
|
import type { FlowPanelMode, FlowPanelPreference } from './panelPlacement'
|
|
|
|
// Anonymous counters for where the flow editor's step panel ends up. Same rules as every
|
|
// other `logFeatureUsage` caller: aggregated counts only, and the three keys below are the
|
|
// whole vocabulary — no path, expression or step id ever reaches here.
|
|
|
|
const FEATURE = 'flow_editor'
|
|
const KIND = 'panel_placement'
|
|
|
|
export type FlowPanelPlacementEvent =
|
|
/** The width moved the panel into the modal, under `auto`. */
|
|
| 'breakpoint_modal'
|
|
/** The user pinned the panel into the pane while it was in the modal. */
|
|
| 'force_attach'
|
|
/** The user pinned the panel out into the modal while it was in the pane. */
|
|
| 'force_detach'
|
|
|
|
type Log = (event: FlowPanelPlacementEvent) => void
|
|
|
|
const log: Log = (event) => logFeatureUsage(FEATURE, KIND, { key: event })
|
|
|
|
/**
|
|
* The event a placement pin should produce, or nothing. `auto` is not a placement being
|
|
* forced, and a pin that matches where the panel already is moves nothing — the counters
|
|
* carry no width, so counting that would be indistinguishable from the override that did
|
|
* move the panel.
|
|
*/
|
|
export function forcedPlacementEvent(
|
|
preference: FlowPanelPreference,
|
|
mode: FlowPanelMode
|
|
): FlowPanelPlacementEvent | undefined {
|
|
if (preference === mode) return undefined
|
|
if (preference === 'docked') return 'force_attach'
|
|
if (preference === 'modal') return 'force_detach'
|
|
return undefined
|
|
}
|
|
|
|
/**
|
|
* Counts the width moving the panel into the modal, once per crossing — `mode` re-resolves
|
|
* continuously as a drag settles, so counting per evaluation would read one drag as hundreds.
|
|
*
|
|
* Both guards below are there because a modal panel is not by itself a crossing: `wasModal`
|
|
* follows where the panel was rather than whether the breakpoint put it there, and an
|
|
* unmeasured editor is no placement at all. Rune-free so each edge is testable directly.
|
|
*/
|
|
export function createBreakpointTracker(emit: Log) {
|
|
let wasModal = false
|
|
|
|
return {
|
|
observe(preference: FlowPanelPreference, mode: FlowPanelMode, measured: boolean) {
|
|
if (!measured) return
|
|
if (mode === 'modal' && !wasModal && preference === 'auto') emit('breakpoint_modal')
|
|
wasModal = mode === 'modal'
|
|
}
|
|
}
|
|
}
|
|
|
|
export interface FlowPanelPlacementTelemetry {
|
|
/** The panel's current placement; emits `breakpoint_modal` on a crossing into the modal. */
|
|
observe(preference: FlowPanelPreference, mode: FlowPanelMode, measured: boolean): void
|
|
/** A placement the user pinned, against where the panel was when they pinned it. */
|
|
forced(preference: FlowPanelPreference, mode: FlowPanelMode): void
|
|
}
|
|
|
|
const CONTEXT_KEY = 'flowPanelPlacementTelemetry'
|
|
|
|
const NOOP: FlowPanelPlacementTelemetry = { observe: () => {}, forced: () => {} }
|
|
|
|
/**
|
|
* Published by `FlowBuilder`, above the `{#key}` that rebuilds the editor on a reload: a
|
|
* tracker recreated mid-edit would re-arm and count a still-narrow editor again.
|
|
*
|
|
* `enabled` is false in session preview tabs. Those stay mounted and laid out at panel width
|
|
* even while hidden, and that panel is narrower than the breakpoint by construction: their
|
|
* crossings would bury the ones this measures, and their overrides would then be read
|
|
* against a denominator that no longer contains them.
|
|
*/
|
|
export function setFlowPanelPlacementTelemetry(enabled: boolean): void {
|
|
const emit: Log = enabled ? log : () => {}
|
|
const tracker = createBreakpointTracker(emit)
|
|
setContext<FlowPanelPlacementTelemetry>(CONTEXT_KEY, {
|
|
observe: tracker.observe,
|
|
forced: (preference, mode) => {
|
|
const event = forcedPlacementEvent(preference, mode)
|
|
if (event) emit(event)
|
|
}
|
|
})
|
|
}
|
|
|
|
export function useFlowPanelPlacementTelemetry(): FlowPanelPlacementTelemetry {
|
|
return getContext<FlowPanelPlacementTelemetry | undefined>(CONTEXT_KEY) ?? NOOP
|
|
}
|