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
21 changes: 21 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,27 @@ When adding new code to covered directories, you must add corresponding tests to

## Important Patterns

### When bumping the app version:
`APP_VERSION` in `src/frontend/data/constants/app-version.ts` is the **single
source of truth** for the human-facing version, shared **byte-for-byte** between
openplc-editor and openplc-web (enforced by the mirror gate / `compare-surfaces.py`).
The About modal renders it directly; the web build writes it into `version.json`.

**Bump `APP_VERSION` — never `package.json` alone.** Make the identical one-line
edit in BOTH repos, and set `package.json.version` to the same value in both so
they can't drift. Roles: `APP_VERSION` is what the user sees in the About dialog;
`package.json.version` is what electron-builder stamps on the desktop binary and
what the release tag `vX.Y.Z` must match. Bumping only `package.json` leaves the
About dialog stuck on the old version — **this mistake shipped 4.2.7 and 4.2.8
with About still showing 4.2.6.** If the two ever disagree, `APP_VERSION` is
authoritative; fix it to match.

Release order: bump `APP_VERSION` + `package.json` (both repos, same value) → PR
to `development` → merge → promote `development`→`main` on both → tag `vX.Y.Z` on
the editor's `main` to trigger the "Build and Release" workflow. Web auto-deploys
on its `main` push. (Ideally `package.json.version` should be derived from
`APP_VERSION` in the release workflow so a single bump can never drift.)

### When adding a new port:
1. Define the interface in `src/middleware/shared/ports/`
2. Add it to `PlatformPorts` in `src/middleware/shared/providers/types.ts`
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import './configs'

import { Editor as PrimitiveEditor } from '@monaco-editor/react'
Expand Down Expand Up @@ -1245,6 +1245,17 @@
minimap: { enabled: false },
dropIntoEditor: { enabled: true },
readOnly: isDebuggerVisible,
// Force Monaco's classic hidden-<textarea> input instead of the newer
// EditContext-API surface (a plain `<div class="native-edit-context">`).
// Monaco 0.54 enables EditContext by default wherever the browser exposes
// the API, but WebKit/Safari's EditContext support is immature: the Tab
// `keydown` on that surface never reaches Monaco's keybinding service, so
// Tab-accept of AI inline suggestions silently no-ops on Safari (mouse
// "Accept" works because it's a direct widget action). The textarea path is
// mature and consistent across Chrome/Safari, restoring Tab-accept. (It also
// makes the surface a real input element that @xyflow's `isInputDOMNode`
// recognises — see the `.nokey` workaround note in the render below.)
editContext: false,
// Lock indentation to 4 spaces across every language Monaco
// hosts (ST / IL / Python / C++). Without this Monaco's
// `detectIndentation` heuristic kicks in on the existing model
Expand Down Expand Up @@ -1287,7 +1298,18 @@
// Keep the LSP dropdown visible alongside the AI ghost text instead of
// suppressing it — coexistence is the whole point here.
suppressSuggestions: false,
},
// Render the AI ghost text EVEN WHILE the LSP suggest widget is open
// with a highlighted item. Monaco defaults `showOnSuggestConflict` to
// 'never', which hides the ghost the instant the dropdown auto-selects
// an entry (which it does on almost every keystroke) — so the ghost
// that Tab is meant to accept would flicker away exactly when the user
// reaches for it. 'always' keeps both surfaces visible; acceptance stays
// split by key (Enter/arrows commit the LSP item, Tab commits the AI
// ghost — see `installAiLspCoexistenceKeybindings`). `experimental` is
// not yet in Monaco's public `IInlineSuggestOptions` type but is read at
// runtime (editorOptions.js), hence the cast.
experimental: { showOnSuggestConflict: 'always' },
} as monacoEditorOptionsType['inlineSuggest'],
}),
}

Expand Down
1 change: 1 addition & 0 deletions src/middleware/shared/ports/ai-port.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
/**
* AIPort — Contract for AI-assisted coding features (inline completions, chat, credits, telemetry).
*
Expand Down Expand Up @@ -57,6 +57,7 @@
| 'completion_dismissed'
| 'completion_error'
| 'completion_timeout'
| 'completion_empty'
| 'chat_message'
| 'chat_rating'
| 'conversation_created'
Expand Down
Loading