Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion skills/q-flow/assets/viewer-dist/index.html

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function useViewerController(graph, theme, panels, moduleColors, original
};
const saveGraph = () => saveGraphJson(graphForSave(layout.currentGraph), graph.meta.locale, setStatus);
const reset = () => {
layout.resetLayout(); selection.resetSelection(); setFlowEnabled(false);
layout.resetLayout(); selection.resetSelection(); setFlowEnabled(true);
setStatus('Reset: original positions and reading view restored');
};
return { ...layout, ...selection, ...presentation, ...panels, ...fullscreen,
Expand Down
2 changes: 1 addition & 1 deletion skills/q-flow/assets/viewer/src/main.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Viewer() {
const [activeType, setActiveType] = useState(diagramTypeOf(diagrams[0]));
const drafts = useRef(new Map());
// Edge motion is opt-in: a running dash reads as the dashed (inferred) notation.
const flowControl = useState(false);
const flowControl = useState(true);
const { preference, setPreference, theme } = useAppearance();
const panels = usePanels();
const originalGraph = diagrams.find(item => diagramTypeOf(item) === activeType) ?? diagrams[0];
Expand Down
2 changes: 1 addition & 1 deletion skills/q-flow/references/viewer-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ Read this section only for Viewer maintenance or interaction audits. Graph autho
- `groupAppearanceMap()` gives every boundary the neutral Slate 2 surface with a Slate 5 hairline and steps directly nested boundaries onto Slate 1 (dark Slate 3); there is no colored accent. Fills are opaque to prevent nested accumulation; render parents before children and keep every group below edges/cards. `groupFrameSvg()` shares the same frame across page, minimap and SVG/PNG. Groups do not inherit module/status colors; names and sequence operators retain meaning when slots repeat. Headings and operators use neutral main ink. Verify edge contrast against group fills as well as the canvas.
- Build the legend from categories and line styles actually present: role swatches for the outlines in view plus one chip-colored entry per module. Use current theme tones. Test diagram text at ≥4.5:1 and meaningful outlines/lines at ≥3:1 against their actual backgrounds, including opacity. Preserve non-color symbols and text for grayscale/color-vision accessibility.
- Open the reading legend from the `Legend` floating button at the canvas's top-left as a popover that also holds the `Edge animation` switch when directed relationships exist. Keep each original symbol before its label, including its shape, theme colors and solid/dashed line style, as plain inline text that wraps within the popover. The button yields to the right of an open navigation panel; pan/zoom leaves its position and text size unchanged, and Escape or an outside click closes the popover.
- With the `Edge animation` switch on (it starts off, because a resting dash would read as the dashed evidence notation), directed relationships show one clearly visible moving dash overlay from source to target; the overlay exists only while it moves, and undirected relationships remain static. Preserve the solid/dashed evidence baseline beneath the overlay. For sequence messages the baseline stays opaque and the 3.2-graph-unit overlay has no glow. Dashed returns / framework / inference messages move the baseline, its same-route `5 5` mask and selection strokes together: the dashes travel from source to target while gaps stay clear at each animation phase. Brightness changes within fixed dash positions are not sufficient. User selection keeps motion running. Switching sequence flow off or reducing motion removes the overlay and stops the baseline's dash phase.
- With the `Edge animation` switch on (the default), directed relationships show one clearly visible moving dash overlay from source to target; the overlay exists only while it moves, and undirected relationships remain static. Preserve the solid/dashed evidence baseline beneath the overlay. For sequence messages the baseline stays opaque and the 3.2-graph-unit overlay has no glow. Dashed returns / framework / inference messages move the baseline, its same-route `5 5` mask and selection strokes together: the dashes travel from source to target while gaps stay clear at each animation phase. Brightness changes within fixed dash positions are not sufficient. User selection keeps motion running. Switching sequence flow off or reducing motion removes the overlay and stops the baseline's dash phase.
- Hover and selection emphasis use outlines and shadows without scaling node geometry or replacing semantic fills and borders. User selection adds one shared 760ms outline/glow rebound to the node and its direct incident edges, then retains static emphasis; only stroke width, opacity, and shadow animate.
- The motion-effects block at the end of `styles.css` supplies clear moving stroke widths without permanent colored glows, tinted node shadows or a colored canvas vignette. All meaningful edge baselines remain opaque. Existing restrained selection feedback remains local; reduced-transparency and high-contrast preferences disable decorative halos without changing notation or baseline contrast.
- Honor reduced-motion preferences by disabling relationship and selection motion, and applying view changes without animation.
Expand Down
18 changes: 5 additions & 13 deletions skills/q-flow/scripts/browser-interactions.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1904,9 +1904,6 @@ async function flowContrastChecks(browser, url, graph) {
await page.goto(url); await chooseGraph(page, graph, false);
await theme(page, colorTheme);
const selected = target(graph); await searchSelect(page, graph, selected);
await openLegend(page); const motion = page.getByRole('switch', { name: '连线流动', exact: true });
if (await motion.count()) await motion.click();
await dismiss(page);
if (graph.meta.diagramType === 'sequence') { await hidePanels(page); await fit(page); }
await page.waitForTimeout(800);
const stable = await geometry(page), sequence = getDiagram(graph.meta.diagramType).sequence;
Expand Down Expand Up @@ -2077,13 +2074,11 @@ async function flowChecks(browser, url, graph) {
else assert.equal(after[i], before[i], 'The dashed stroke stays still with flow disabled or reduced motion.');
}
};
assert.equal(await count(page, '.edge-flow'), 0, 'Edge motion starts off: a resting dash would read as the dashed notation.');
assert.equal(await count(page, '.edge-flow'), directed.length, 'Edge motion starts on.');
assert.equal(await count(page, '.capsule,.playback-controls,.playback-outline,.diagram-node.is-current,.diagram-node.is-complete'), 0);
assert.equal(await page.getByRole('button', { name: /^(播放|暂停|← 上一步|下一步 →)$/ }).count(), 0);
await searchSelect(page, graph, target(graph));
if (directed.length) {
await openLegend(page); await page.getByRole('switch', { name: '连线流动', exact: true }).click(); await dismiss(page);
assert.equal(await count(page, '.edge-flow'), directed.length);
await assertDashMotion(true);
const flow = page.locator('.edge-flow').first();
const before = await flow.evaluate(element => getComputedStyle(element).strokeDashoffset);
Expand All @@ -2092,12 +2087,11 @@ async function flowChecks(browser, url, graph) {
await openLegend(page); await page.getByRole('switch', { name: '连线流动', exact: true }).click(); await dismiss(page);
assert.equal(await count(page, '.edge-flow'), 0, 'Stopping flow removes the overlay instead of freezing it.');
await assertDashMotion(false);
await openLegend(page); await page.getByRole('switch', { name: '连线流动', exact: true }).click(); await dismiss(page);
await openMore(page); await menuItem(page, '重置').click();
assert.equal(await count(page, '.edge-flow'), 0, 'Reset returns edge motion to its default, off.');
await assertDashMotion(false);
await page.waitForFunction(n => document.querySelectorAll('.edge-flow').length === n, directed.length, { timeout: 3000 }).catch(() => {});
assert.equal(await count(page, '.edge-flow'), directed.length, 'Reset returns edge motion to its default, on.');
await assertDashMotion(true);
if (graph.meta.diagramType === 'sequence') {
await openLegend(page); await page.getByRole('switch', { name: '连线流动', exact: true }).click(); await dismiss(page);
await page.emulateMedia({ reducedMotion: 'reduce' });
await page.waitForFunction(() => !document.querySelector('.edge-flow')); await assertDashMotion(false);
await page.emulateMedia({ reducedMotion: 'no-preference' });
Expand Down Expand Up @@ -2227,7 +2221,6 @@ async function sequenceReadingChecks(browser, url, graph) {
await nodeElement(page,graph.nodes[0].id).focus(); await page.keyboard.press('Enter'); if(mobile(page)) await hidePanels(page);
const preferenceSelection = await page.locator('.diagram-node.is-selected').evaluate(el=>el.closest('[data-id]').dataset.id), preferencePanels = await panelState(page);
await page.emulateMedia({reducedMotion:'no-preference'});
await openLegend(page);await page.getByRole('switch',{name:'连线流动',exact:true}).click();await dismiss(page);
await page.waitForFunction(()=>document.querySelectorAll('.sequence-edge-flow').length>0);
await openLegend(page);const toggle=page.getByRole('switch',{name:'连线流动',exact:true});
const state=await page.locator('.react-flow__viewport').getAttribute('style');
Expand Down Expand Up @@ -2358,8 +2351,7 @@ async function fileUrlSequenceCheck(browser, graph) {
}
await openLegend(page); const flow = page.getByRole('switch', { name: '连线流动', exact: true });
if (await flow.count()) {
await flow.click(); assert.ok(await count(page, '.edge-flow') > 0, 'The file page draws sequence overlays when flow is on.');
await flow.click(); assert.equal(await count(page, '.edge-flow'), 0, 'The file page removes sequence overlays when flow is off.');
await flow.click(); assert.equal(await count(page, '.edge-flow'), 0, 'The file page removes sequence overlays when flow is off.'); await flow.click();
}
await dismiss(page); await assertFlow(page);
assert.deepEqual(external, [], 'The copied file page makes no external requests.'); assert.deepEqual(failed, [], 'The copied file page has no failed requests.');
Expand Down
Loading