Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
24cf03e
feat: colour-coding arrows
Feb 9, 2026
b0cccb0
feat: removed colour-coding for arrows (all white), implemented featu…
Feb 9, 2026
151c4e8
fix: removed whitespace
Feb 9, 2026
1a2a294
Merge branch 'source-academy:master' into master
Feb 10, 2026
cd9ec5b
fix: bugfix and linting
Feb 12, 2026
a6ffa89
test
RogerZhang888 Feb 19, 2026
4cde278
Merge branch 'master' into master
Feb 19, 2026
b9c62d8
test2
RogerZhang888 Feb 19, 2026
8675d11
Merge branch 'master' of https://github.com/wjh3355/source-frontend-fork
RogerZhang888 Feb 19, 2026
1de724b
fix: Refactor arrow selection management and clean up code style
Feb 19, 2026
7cf2e8c
fix: ran linter
Feb 19, 2026
1b15c73
fix: snapshots
Feb 19, 2026
889b5d0
fix: change to american english for the word "color"
Feb 19, 2026
4055da7
fix: faded arrows now stay faded
Feb 19, 2026
0fccc0c
Merge remote-tracking branch 'upstream/master'
Mar 13, 2026
1856ae7
test
RogerZhang888 Mar 13, 2026
e14db30
test
RogerZhang888 Mar 13, 2026
5770e18
test
RogerZhang888 Mar 13, 2026
99f360b
fix: prematurely terminating arrows
RogerZhang888 Mar 13, 2026
50225ce
added global MinTerminalSegmentLength
RogerZhang888 Mar 13, 2026
99bc4ed
fix start
RogerZhang888 Mar 13, 2026
6b93490
added arrows from function objects to their body; added filter for di…
Mar 14, 2026
d81af57
Merge branch 'master' of github.com:wjh3355/source-frontend-fork
Mar 14, 2026
03f4382
made arrows highlight to red in printable mode; added more filters fo…
Mar 14, 2026
2613f9d
standardardised radius of curvature for arrows
RogerZhang888 Mar 20, 2026
aaf878c
Merge remote-tracking branch 'upstream/master' into rz
RogerZhang888 Mar 29, 2026
43dbdaf
QOL: improved arrow bending
RogerZhang888 Mar 30, 2026
1cdf263
Merge remote-tracking branch 'upstream' into rz
RogerZhang888 Mar 31, 2026
9e78919
updates
RogerZhang888 Mar 31, 2026
107a5cc
update
RogerZhang888 Mar 31, 2026
3053b33
update
RogerZhang888 Mar 31, 2026
76bd08f
update
RogerZhang888 Mar 31, 2026
0b2b815
update
RogerZhang888 Mar 31, 2026
5379d48
update
RogerZhang888 Mar 31, 2026
45d8428
update
RogerZhang888 Mar 31, 2026
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
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,37 @@ exports[`CSE Machine component renders correctly 1`] = `
</span>
</a>
</span>
<span
class="bp6-popover-target"
>
<a
aria-disabled="true"
aria-expanded="false"
aria-haspopup="menu"
class="bp6-button bp6-disabled"
disabled=""
role="button"
tabindex="-1"
>
<span
aria-hidden="true"
class="bp6-icon bp6-icon-flow-branch"
>
<svg
data-icon="flow-branch"
height="16"
role="img"
viewBox="0 0 16 16"
width="16"
>
<path
d="M10.643 6.595c.22.418.344.894.344 1.399 0 .439-.094.855-.263 1.231l3.265 3.462-.002-1.75a.97.97 0 01.314-.68.99.99 0 011.388.048c.186.2.316.46.3.715l-.009 4.03a.96.96 0 01-.3.68.97.97 0 01-.698.266l-4.053.002a.97.97 0 01-.679-.314 1.03 1.03 0 01.05-1.42.97.97 0 01.698-.266l1.7-.001-3.305-3.35a2.998 2.998 0 01-4.228-1.653H.999a1 1 0 010-2h4.166a3 3 0 014.06-1.735l3.449-3.268-1.745.002a.979.979 0 01-.631-1.692c.199-.186.459-.316.713-.3l4.025.009c.247.008.493.1.679.3s.274.451.265.7l.002 4.046a.97.97 0 01-.313.68 1.03 1.03 0 01-1.42-.05.97.97 0 01-.266-.7V3.295z"
fill-rule="evenodd"
/>
</svg>
</span>
</a>
</span>
</div>
<div
class="bp6-button-group"
Expand Down
59 changes: 58 additions & 1 deletion src/commons/sideContent/content/SideContentCseMachine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
Checkbox,
Classes,
Divider,
Popover,
Position,
Slider,
Tooltip
} from '@blueprintjs/core';
Expand Down Expand Up @@ -43,6 +45,7 @@
stepLimitExceeded: boolean;
chapter: Chapter;
clearDeadFrames: boolean;
arrowFilterOpen: boolean;
};

type CseMachineProps = OwnProps & StateProps & DispatchProps;
Expand Down Expand Up @@ -84,7 +87,8 @@
lastStep: false,
stepLimitExceeded: false,
chapter: props.chapter,
clearDeadFrames: false
clearDeadFrames: false,
arrowFilterOpen: false
};
if (this.isJava()) {
JavaCseMachine.init(
Expand Down Expand Up @@ -198,6 +202,7 @@
}

public render() {
const arrowFilters = CseMachine.getArrowOriginFilters();
const hotkeyBindings: HotkeyItem[] = this.state.visualization
? [
['a', this.stepFirst],
Expand Down Expand Up @@ -298,6 +303,48 @@
/>
</AnchorButton>
</Tooltip>
<Popover
isOpen={this.state.arrowFilterOpen}
onInteraction={nextOpen => this.setState({ arrowFilterOpen: nextOpen })}
position={Position.BOTTOM_LEFT}
content={
<div style={{ padding: '8px 10px', minWidth: '210px' }}>
<div style={{ marginBottom: '8px', fontWeight: 600 }}>Filter Arrows</div>
<Checkbox
checked={arrowFilters.text}
label="From text"
onChange={() => this.toggleArrowFilter('text')}
/>
<Checkbox
checked={arrowFilters.frame}
label="From frames"
onChange={() => this.toggleArrowFilter('frame')}
/>
<Checkbox
checked={arrowFilters.function}
label="From function objects"
onChange={() => this.toggleArrowFilter('function')}
/>
<Checkbox
checked={arrowFilters.control}
label="From control"
onChange={() => this.toggleArrowFilter('control')}
/>
<Checkbox
checked={arrowFilters.stash}
label="From stash"
onChange={() => this.toggleArrowFilter('stash')}
/>
<Checkbox
checked={arrowFilters.arrayUnit}
label="From array units"
onChange={() => this.toggleArrowFilter('arrayUnit')}
/>
</div>
}
>
<AnchorButton icon="flow-branch" disabled={!this.state.visualization} />
</Popover>
</ButtonGroup>
)}
<ButtonGroup>
Expand Down Expand Up @@ -535,6 +582,16 @@
this.sliderShift(0);
this.sliderRelease(0);
};

private toggleArrowFilter = (
origin: 'text' | 'frame' | 'function' | 'control' | 'stash' | 'arrayUnit'
) => {
const filters = CseMachine.getArrowOriginFilters();
CseMachine.setArrowOriginVisible(origin, !filters[origin]);
CseMachine.clearCachedLayouts();
CseMachine.redraw();
this.forceUpdate();
};
}

const mapStateToProps: MapStateToProps<StateProps, OwnProps, OverallState> = (
Expand Down Expand Up @@ -592,7 +649,7 @@
dispatch
);

export const SideContentCseMachine = connect(

Check warning on line 652 in src/commons/sideContent/content/SideContentCseMachine.tsx

View workflow job for this annotation

GitHub Actions / lint (eslint)

Fast refresh only works when a file only exports components. Use a new file to share constants or functions between components
mapStateToProps,
mapDispatchToProps
)(SideContentCseMachineBase);
Expand Down
23 changes: 22 additions & 1 deletion src/features/cseMachine/CseMachine.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import React from 'react';

import { arrowSelection } from './components/arrows/ArrowSelection';
import { Layout, LayoutCache } from './CseMachineLayout';
import { EnvTree, EnvTreeNode } from './CseMachineTypes';
import { ArrowOriginFilterKey, ArrowOriginFilters, EnvTree, EnvTreeNode } from './CseMachineTypes';
import { deepCopyTree, getEnvId } from './CseMachineUtils';

type SetVis = (vis: React.ReactNode) => void;
Expand All @@ -29,6 +29,14 @@ export default class CseMachine {
private static stackTruncated: boolean = false;
private static centerAlignment: boolean = false; // added for center alignment
private static centerAlignmentToggled: boolean = false;
private static arrowOriginFilters: ArrowOriginFilters = {
text: true,
frame: true,
function: true,
control: true,
stash: true,
arrayUnit: true
};
private static environmentTree: EnvTree | undefined;
private static currentEnvId: string;
private static control: Control | undefined;
Expand Down Expand Up @@ -80,6 +88,19 @@ export default class CseMachine {
public static getCenterAlignment(): boolean {
return CseMachine.centerAlignment;
}

public static getArrowOriginFilters(): ArrowOriginFilters {
return { ...CseMachine.arrowOriginFilters };
}

public static isArrowOriginVisible(origin: ArrowOriginFilterKey | null): boolean {
if (origin === null) return true;
return CseMachine.arrowOriginFilters[origin];
}

public static setArrowOriginVisible(origin: ArrowOriginFilterKey, visible: boolean): void {
CseMachine.arrowOriginFilters[origin] = visible;
}
public static getMasterLayout(): LayoutCache | null {
return CseMachine.getPrintableMode()
? CseMachine.printLayoutCache
Expand Down
7 changes: 5 additions & 2 deletions src/features/cseMachine/CseMachineConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export const Config = Object.freeze({
FramePaddingX: 20,
FramePaddingY: 30,
FrameMinGapX: 80,
FrameMarginX: 30,
FrameMarginX: 50,
FrameMarginY: 10,
FrameCornerRadius: 3,

Expand Down Expand Up @@ -44,12 +44,15 @@ export const Config = Object.freeze({
ArrowHitStrokeWidth: 5,
ArrowHoveredStrokeWidth: 2,
ArrowHoveredHeadSize: 15,
ArrowCornerRadius: 40,
ArrowCornerRadius: 20,
ArrowMinCornerRadius: 5,
ArrowSmallBendRadiusScale: 1,

MaxExportWidth: 20000,
MaxExportHeight: 12000,

MinTerminalSegmentLength: 30,
ArrowPostFrameStraightLength: 20,
// Canvas background color
BgColor: '#2c3e50',
PrintBgColor: '#fff',
Expand Down
12 changes: 12 additions & 0 deletions src/features/cseMachine/CseMachineTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,3 +114,15 @@ export type ReferenceType = Binding | ArrayUnit;

/** type of an array of steps (as defined by a function), for the arrow classes */
export type StepsArray = Array<(x: number, y: number) => [number, number]>;

/** categories for filtering arrows by source/origin */
export type ArrowOriginFilterKey =
| 'text'
| 'frame'
| 'function'
| 'control'
| 'stash'
| 'arrayUnit';

/** visibility map for arrow origin categories */
export type ArrowOriginFilters = Record<ArrowOriginFilterKey, boolean>;
12 changes: 6 additions & 6 deletions src/features/cseMachine/__tests__/CseMachineAnimation.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,11 @@ test('AnimationComponent animates correctly with parallel animateTo calls 1', as
expected: elapsed => {
const d = CseAnimation.defaultDuration;
return {
x: [CseAnimation.defaultEasing(Math.min(elapsed / d, 1), 0, 100, 1), 1],
y: [CseAnimation.defaultEasing(Math.min(Math.max(0, elapsed / d - 0.5), 1), 0, 100, 1), 1],
x: [CseAnimation.defaultEasing(Math.min(elapsed / d, 1), 0, 100, 1), 6],
y: [CseAnimation.defaultEasing(Math.min(Math.max(0, elapsed / d - 0.5), 1), 0, 100, 1), 6],
opacity: [
CseAnimation.defaultEasing(Math.min(Math.max(0, elapsed / d / 0.75 - 1 / 3), 1), 0, 1, 1),
0.01
0.08
]
};
}
Expand All @@ -197,10 +197,10 @@ test('AnimationComponent animates correctly with parallel animateTo calls 2', as
return {
x:
elapsed < d
? [easing(elapsed, 0, 100, d), 4]
? [easing(elapsed, 0, 100, d), 10]
: elapsed < d * 2
? [easing(elapsed - d, 100, 100, d), 4]
: [easing(elapsed - d * 2, 200, -50, d), 2]
? [easing(elapsed - d, 100, 100, d), 10]
: [easing(elapsed - d * 2, 200, -50, d), 6]
};
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2383,7 +2383,7 @@ exports[`CSE Machine Control Stash correctly renders: global environments are tr
}
>
<Path
data="M 181.5 40 L 349 330 L 349 330 "
data="M 181.5 40 L 369 330 L 369 330 "
hitStrokeWidth={10}
preventDefault={false}
ref={
Expand All @@ -2401,7 +2401,7 @@ exports[`CSE Machine Control Stash correctly renders: global environments are tr
[
181.5,
40,
349,
369,
330,
]
}
Expand Down
5 changes: 4 additions & 1 deletion src/features/cseMachine/components/Binding.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,10 @@ export class Binding extends Visible {
: (this.value.height() - Config.FontSize) / 2;

this.keyYOffset = keyYOffset;
this.key = new Text(this.keyString, this.x(), this.y() + keyYOffset, { faded: !this.isLive });
this.key = new Text(this.keyString, this.x(), this.y() + keyYOffset, {
faded: !this.isLive,
parentFrame: this.frame
});

const printFnDescriptionHeight =
CseMachine.getPrintableMode() &&
Expand Down
1 change: 1 addition & 0 deletions src/features/cseMachine/components/Text.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ export interface TextOptions {
isStringIdentifiable: boolean;
faded: boolean;
hidden: boolean;
parentFrame?: any; // Reference to the frame this text belongs to

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

medium

Using any for parentFrame bypasses TypeScript's type checking. Since this property is used to access coordinates and dimensions (e.g., in ArrowFromText.tsx), it should be typed as IVisible or Frame to ensure type safety.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@RichDom2185 has also advised me in another of my recent PRs to not use the 'any' type in general. The exact comment with his message is here.

#3690 (comment)

It might be outdated! Do expand the "outdated" comment tabs

}

export const defaultOptions: TextOptions = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ export class ArrowFromArrayUnit extends GenericArrow<ArrayUnit, Value> {
this.isLive = this.source.parent.isEnclosingFrameLive();
}

protected getOriginFilterKey() {
return 'arrayUnit' as const;
}

protected calculateSteps() {
const from = this.source;
const to = this.target;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export class ArrowFromControlItemComponent extends GenericArrow<
this.isLive = true; // Control items are always live
}

protected getOriginFilterKey() {
return 'control' as const;
}

protected calculateSteps() {
const from = this.source;
const to = this.target;
Expand Down
5 changes: 5 additions & 0 deletions src/features/cseMachine/components/arrows/ArrowFromFn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export class ArrowFromFn extends GenericArrow<FnValue | GlobalFnValue | ContValu
this.isLive = this.source.isLive();
}
}

protected getOriginFilterKey() {
return 'function' as const;
}

protected calculateSteps() {
const from = this.source;
const to = this.target;
Expand Down
35 changes: 35 additions & 0 deletions src/features/cseMachine/components/arrows/ArrowFromFnToBody.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { Config } from '../../CseMachineConfig';
import { StepsArray } from '../../CseMachineTypes';
import { FnValue } from '../values/FnValue';
import { GlobalFnValue } from '../values/GlobalFnValue';
import { FnBodyTarget } from './FnBodyTarget';
import { GenericArrow } from './GenericArrow';

/** Arrow from first function circle to its description tooltip. */
export class ArrowFromFnToBody extends GenericArrow<FnValue | GlobalFnValue, FnBodyTarget> {
constructor(from: FnValue | GlobalFnValue) {
super(from);
this.isLive = from instanceof GlobalFnValue ? true : from.isLive();
}

protected updateIsLive(): void {
this.isLive = this.source instanceof GlobalFnValue ? true : this.source.isLive();
}

protected isInteractive(): boolean {
return false;
}

protected getOriginFilterKey() {
return 'function' as const;
}

protected calculateSteps(): StepsArray {
const to = this.target;
if (!to) return [];

const targetY = to.y();

return [(x, y) => [x + Config.FnRadius, y], (x, _y) => [x, targetY]];
}
}
4 changes: 4 additions & 0 deletions src/features/cseMachine/components/arrows/ArrowFromFrame.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ export class ArrowFromFrame extends GenericArrow<Frame, Frame> {
this.isLive = this.source.environment && Layout.liveEnvIDs.has(this.source.environment.id);
}

protected getOriginFilterKey() {
return 'frame' as const;
}

protected calculateSteps() {
const to = this.target;
if (!to) return [];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export class ArrowFromStashItemComponent extends GenericArrow<
this.isLive = true; // Stash items are always live
}

protected getOriginFilterKey() {
return 'stash' as const;
}

protected calculateSteps() {
const from = this.source;
const to = this.target;
Expand Down
Loading
Loading