Skip to content
Open
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
5 changes: 5 additions & 0 deletions .changeset/clear-code-editor-guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashintel/petrinaut-core": patch
---

Make the code editor guide available to the in-app assistant.
5 changes: 5 additions & 0 deletions .changeset/tidy-code-editor-layouts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@hashintel/petrinaut": patch
---

Open model functions in full screen, the properties panel, or a bottom dock with remembered layouts and direct navigation.
3 changes: 3 additions & 0 deletions libs/@hashintel/petrinaut-core/src/ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ export const petrinautDocNames = [
"preview",
"ai-assistant",
"visual-settings",
"code-editor",
"compilation-output",
"examples",
] as const;
Expand Down Expand Up @@ -135,6 +136,8 @@ export const petrinautDocSummaries: Record<PetrinautDocName, string> = {
"Compact read-only PetrinautPreview for host-controlled embeds: shared SDCPN canvas, pan/zoom/fit/minimap, selection and responsive inspector, root/subnet navigation, URL-state ownership, omitted editing and management UI, and host-owned iframe security.",
"ai-assistant":
"In-app AI assistant: opening the panel, one text and Voice mode transcript/composer, waveform start, inline Voice state and provenance, typed handoff, consent/recovery, prompt chips, tool cards, read-only/simulate-mode rules, host configuration.",
"code-editor":
"Full-screen, properties-panel and bottom-dock code editors: feature flags, direct function navigation, remembered layout, automatic edits and read-only behavior.",
"visual-settings":
"Animations, keep-panels-mounted, minimap, snap-to-grid, compact vs classic nodes, partial selection, tree view, arc rendering style, compute backend, compilation output, parameter sweeps, in-browser optimization.",
"compilation-output":
Expand Down
1 change: 1 addition & 0 deletions libs/@hashintel/petrinaut/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Petrinaut has three global modes in the top bar, though **Actual** is only enabl
- [Actual Mode](actual-mode.md) -- View a host-provided live Petri net execution, currently via Brunch.
- [Embedded Preview](preview.md) -- Explore a compact, read-only Petri net embedded in a host application.
- [AI Assistant](ai-assistant.md) -- Build, review, and revise nets with text or inline Voice mode.
- [Code Editor](code-editor.md) -- Open model functions in full screen, the properties panel, or a bottom dock.
- [Visual Settings](visual-settings.md) -- Configure the editor appearance and behavior.
- [Compilation Output](compilation-output.md) -- Inspect how your net's code compiled, and what stops it running on the GPU.
- [Examples](examples.md) -- Walkthrough of the built-in example nets.
29 changes: 29 additions & 0 deletions libs/@hashintel/petrinaut/docs/code-editor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Code Editor

Open a model’s code in a larger editor while keeping its place, transition, or differential equation in view.

## Enable the layouts

Open **Settings** using the canvas gear button. Under **Feature flags**, turn on **Code editor layouts**, or select a layout card:

- **Full screen** replaces the canvas with code. The file’s left sidebar stays available.
- **Properties panel** fills the right-hand panel with code and a compact header. Drag its left edge to resize it.
- **Bottom dock** opens code below the canvas. Drag its top edge to resize it.

Your chosen layout is remembered. Turn the feature off to return to the inline editors.

## Open code directly

Use the **Code** menu on the canvas to choose a transition’s predicate or stochastic rate (λ), its transition kernel, a differential equation, or an enabled place visualizer.

You can also select an item in the canvas or left sidebar, then use the code section’s **More options** menu to open it in a specific layout. This works while the section is collapsed. Inside an expanded code section, **Open code editor** uses your preferred layout.

The item’s menu in the left sidebar opens its code directly. For a place with dynamics, **Open equation code** beside its assigned differential equation opens that equation.

## Navigate and edit

The header shows the item’s kind and name. Open the name menu to jump to another function in the active net. Use the function buttons below the name to switch between a transition’s firing function and kernel.

The **Code editor layout** button switches between layouts. Your code, undo history, cursor position, and diagnostics carry over. Enlarged editors show line numbers and wrap long lines; full screen and the bottom dock also show a code minimap.

Changes apply automatically to the model. Read-only files and simulation modes keep code read-only. Use **Back to properties** or **Close code editor** to return to the canvas and property fields. Changing the file or active subnet closes the editor.
6 changes: 6 additions & 0 deletions libs/@hashintel/petrinaut/docs/visual-settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,9 @@ Choose how the toolbar at the bottom of the canvas is laid out. Every layout kee
| **Split** | Two glass segments: the cursor and editing tools on the left, the panel toggle, diagnostics status and playback controls on the right. (Default) |
| **Mode switch** | One segment showing one face at a time. The edit face holds the AI assistant and the tools that add nodes; the simulate face holds diagnostics, Play, the playback speed and the scrubber. The Edit / Simulate switch at the right end flips between them, and starting a run flips to the simulate face by itself. The cursor button is tinted blue on the edit face and purple on the simulate face. |
| **Single** | One segment holding every control in one row: cursor, AI assistant and editing tools, then diagnostics status, a filled blue Play button, the scrubber and the playback settings, with the panel toggle at the far end. |

## Feature flags

### Code editor layouts

Enable **Code editor layouts** to open model functions in **Full screen**, **Properties panel**, or **Bottom dock**. Select a visual layout card to enable the feature and set the preferred layout. The choice is saved between sessions. See [Code Editor](code-editor.md) for navigation and editing.
13 changes: 13 additions & 0 deletions libs/@hashintel/petrinaut/src/react/state/user-settings-context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import type {

export type AutomaticArcRendering = "curved" | "square";

export type CodeEditorPlacement = "fullscreen" | "properties" | "bottom";

export type ArcRendering = "smoothstep" | "bezier" | "custom";

/**
Expand Down Expand Up @@ -79,6 +81,8 @@ export type UserSettings = {
partialSelection: boolean;
enableNetComponents: boolean;
enableNotebookView: boolean;
enableCodeEditorWorkspace: boolean;
codeEditorPlacement: CodeEditorPlacement;
/**
* Persisted preference controlling whether the product walkthrough opens
* automatically the next time the app initializes. The live open state is
Expand Down Expand Up @@ -158,6 +162,9 @@ export type UserSettingsActions = {
setPartialSelection: (value: boolean) => void;
setEnableNetComponents: (value: boolean) => void;
setEnableNotebookView: (value: boolean) => void;

setEnableCodeEditorWorkspace: (value: boolean) => void;
setCodeEditorPlacement: (value: CodeEditorPlacement) => void;
setShowWalkthroughOnInit: (value: boolean) => void;
setWebGpuEnabled: (value: boolean) => void;
setShowCompilationOutput: (value: boolean) => void;
Expand Down Expand Up @@ -198,6 +205,9 @@ export const defaultUserSettings: UserSettings = {
partialSelection: true,
enableNetComponents: false,
enableNotebookView: false,

enableCodeEditorWorkspace: false,
codeEditorPlacement: "fullscreen",
showWalkthroughOnInit: true,
webGpuEnabled: false,
showCompilationOutput: false,
Expand Down Expand Up @@ -237,6 +247,9 @@ export const defaultUserSettingsContextValue: UserSettingsContextValue = {
setPartialSelection: () => {},
setEnableNetComponents: () => {},
setEnableNotebookView: () => {},

setEnableCodeEditorWorkspace: () => {},
setCodeEditorPlacement: () => {},
setShowWalkthroughOnInit: () => {},
setWebGpuEnabled: () => {},
setShowCompilationOutput: () => {},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,3 +226,37 @@ describe("UserSettingsProvider", () => {
expect(screen.getByRole("button", { name: "editor: on" })).toBeTruthy();
});
});

const CodeLayoutProbe = () => {
const settings = use(UserSettingsContext);
return (
<button
type="button"
onClick={() => {
settings.setEnableCodeEditorWorkspace(true);
settings.setCodeEditorPlacement("bottom");
}}
>
{settings.enableCodeEditorWorkspace
? settings.codeEditorPlacement
: "inline"}
</button>
);
};

it("remembers the code editor flag and placement across sessions", () => {
const view = render(
<UserSettingsProvider>
<CodeLayoutProbe />
</UserSettingsProvider>,
);
fireEvent.click(screen.getByRole("button", { name: "inline" }));
expect(screen.getByRole("button", { name: "bottom" })).toBeTruthy();
view.unmount();
render(
<UserSettingsProvider>
<CodeLayoutProbe />
</UserSettingsProvider>,
);
expect(screen.getByRole("button", { name: "bottom" })).toBeTruthy();
});
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import type {
import type {
ArcRendering,
BottomBarVariant,
CodeEditorPlacement,
SubViewSectionSettings,
UserSettings,
} from "./user-settings-context";
Expand Down Expand Up @@ -63,6 +64,11 @@ const loadSettings = (): UserSettings => {
return {
...defaultUserSettings,
...parsed,
codeEditorPlacement:
parsed.codeEditorPlacement === "properties" ||
parsed.codeEditorPlacement === "bottom"
? parsed.codeEditorPlacement
: "fullscreen",
// Someone who had selected the GPU globally keeps it available.
webGpuEnabled: parsed.webGpuEnabled ?? computeBackend === "webgpu",
// A layout this build no longer has falls back to the default one.
Expand Down Expand Up @@ -142,6 +148,13 @@ const OwnedUserSettingsProvider: React.FC<React.PropsWithChildren> = ({
setState((prev) => ({ ...prev, partialSelection: value })),
setEnableNetComponents: (value: boolean) =>
setState((prev) => ({ ...prev, enableNetComponents: value })),
setEnableCodeEditorWorkspace: (value: boolean) =>
setState((previous) => ({
...previous,
enableCodeEditorWorkspace: value,
})),
setCodeEditorPlacement: (value: CodeEditorPlacement) =>
setState((previous) => ({ ...previous, codeEditorPlacement: value })),
setEnableNotebookView: (value: boolean) =>
setState((prev) => ({ ...prev, enableNotebookView: value })),
setShowWalkthroughOnInit: (value: boolean) =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import { use } from "react";

import { Toggle } from "@hashintel/ds-components";
import { css } from "@hashintel/ds-helpers/css";

import { UserSettingsContext } from "../../react/state/user-settings-context";

const layouts = [
{
value: "fullscreen",
label: "Full screen",
description: "Replace the canvas",
},
{
value: "properties",
label: "Properties panel",
description: "Keep the canvas beside code",
},
{
value: "bottom",
label: "Bottom dock",
description: "Keep the canvas above code",
},
] as const;

const headingStyle = css({
display: "flex",
alignItems: "center",
justifyContent: "space-between",
fontSize: "sm",
fontWeight: "medium",
color: "neutral.fg.heading",
gap: "2",
});
const descriptionStyle = css({
fontSize: "xs",
color: "neutral.fg.subtle",
marginTop: "1",
marginBottom: "3",
lineHeight: "[1.5]",
});
const gridStyle = css({
display: "grid",
gridTemplateColumns: "[repeat(3, minmax(0, 1fr))]",
gap: "2",
marginBottom: "3",
});
const cardStyle = css({
padding: "2",
borderWidth: "thin",
borderColor: "neutral.bd.subtle",
borderRadius: "lg",
textAlign: "left",
cursor: "pointer",
background: "neutral.s00",
fontSize: "xs",
color: "neutral.fg.heading",
_hover: { background: "bg.subtle" },
_focusVisible: { outline: "[2px solid #2563eb]", outlineOffset: "[2px]" },
"&[aria-pressed=true]": { borderColor: "[#3b82f6]", background: "[#eff6ff]" },
});
const previewStyle = css({
height: "[60px]",
position: "relative",
overflow: "hidden",
borderRadius: "sm",
borderWidth: "thin",
borderColor: "neutral.bd.subtle",
background: "[#f5f6f8]",
marginBottom: "2",
});
const barStyle = css({
position: "absolute",
inset: "[0 0 auto]",
height: "[7px]",
background: "[#e2e5eb]",
});
const sidebarStyle = css({
position: "absolute",
top: "[7px]",
bottom: "[0]",
width: "[16%]",
borderRight: "[1px solid #e2e5eb]",
background: "neutral.s00",
});
const codeStyle = css({
position: "absolute",
padding: "[4px]",
background: "neutral.s00",
border: "[1px solid #c6d9fa]",
color: "[#4d7cc0]",
fontSize: "[8px]",
lineHeight: "[9px]",
fontFamily: "[monospace]",
whiteSpace: "pre",
overflow: "hidden",
});
const labelStyle = css({ fontWeight: "medium", lineHeight: "[1.3]" });

export const CodeEditorLayoutSettings = () => {
const {
enableCodeEditorWorkspace,
setEnableCodeEditorWorkspace,
codeEditorPlacement,
setCodeEditorPlacement,
} = use(UserSettingsContext);
return (
<>
<div className={headingStyle}>
<span>Code editor layouts</span>
<Toggle
aria-label="Code editor layouts"
size="sm"
value={enableCodeEditorWorkspace}
onChange={setEnableCodeEditorWorkspace}
/>
</div>
<p className={descriptionStyle}>
Open code directly from a field’s menu or the canvas Code menu. Choose
where it opens.
</p>
<div className={gridStyle} aria-label="Default code editor layout">
{layouts.map((layout) => (
<button
key={layout.value}
type="button"
className={cardStyle}
aria-pressed={
enableCodeEditorWorkspace && codeEditorPlacement === layout.value
}
title={layout.description}
onClick={() => {
setCodeEditorPlacement(layout.value);
setEnableCodeEditorWorkspace(true);
}}
>
<div className={previewStyle} aria-hidden="true">
<div className={barStyle} />
<div className={sidebarStyle} />
<div
className={codeStyle}
style={{
top: layout.value === "bottom" ? "45%" : 7,
bottom: 0,
left: layout.value === "properties" ? "50%" : "16%",
right: 0,
}}
>
{"λ function\n return {\n tokens\n }"}
</div>
</div>
<span className={labelStyle}>{layout.label}</span>
</button>
))}
</div>
</>
);
};
Loading
Loading