ReviewOS

also looking at this

craft-native/craft

docs(menu): delete the dead NativeMenus class, document the real menu API

#36
Merged glennmichael123 wants to merge fix/remove-dead-native-menus into main
3 files +188 -59
docs/BRIDGE_API.mdmodified+129-0
Changes to docs/BRIDGE_API.md
@@ -7,6 +7,7 @@ The Craft JavaScript Bridge provides a seamless interface for your web applicati
77- [Overview](#overview)
88- [Getting Started](#getting-started)
99- [System Tray API](#system-tray-api)
10- [Application Menu (macOS)](#application-menu-macos)
1011- [Window API](#window-api)
1112- [App API](#app-api)
1213- [TypeScript Support](#typescript-support)
@@ -157,6 +158,134 @@ interface MenuItem {
157158}
158159```
159160
161## Application Menu (macOS)
162
163The menu bar across the top of the screen, distinct from the tray menu above.
164macOS only — on Linux and Windows these calls are accepted and ignored.
165
166Craft installs a default bar at startup, so an app that never touches this API
167still gets the standard shortcuts. Calling `set()` replaces that bar
168**entirely** — if your app has text input anywhere, declare an Edit menu
169yourself, or Cmd+X/C/V stop reaching it.
170
171### `window.craft.menu.set(options: { menus: MenuDefinition[] }): Promise<void>`
172
173Replace the whole bar.
174
175```javascript
176await window.craft.menu.set({
177 menus: [
178 {
179 label: 'File',
180 items: [
181 { id: 'new', label: 'New', shortcut: 'cmd+n' },
182 { id: 'open', label: 'Open...', shortcut: 'cmd+o' },
183 { separator: true },
184 { role: 'close', label: 'Close Window', shortcut: 'cmd+w' },
185 ],
186 },
187 {
188 label: 'Edit',
189 items: [
190 { role: 'undo', label: 'Undo', shortcut: 'cmd+z' },
191 { role: 'redo', label: 'Redo', shortcut: 'cmd+shift+z' },
192 { separator: true },
193 { role: 'cut', label: 'Cut', shortcut: 'cmd+x' },
194 { role: 'copy', label: 'Copy', shortcut: 'cmd+c' },
195 { role: 'paste', label: 'Paste', shortcut: 'cmd+v' },
196 { role: 'selectAll', label: 'Select All', shortcut: 'cmd+a' },
197 ],
198 },
199 ],
200});
201```
202
203The returned promise resolves once the message has been posted, not once the
204menu exists — native acknowledges nothing on success. A failure arrives on the
205console rather than as a rejection.
206
207### `window.craft.menu.onAction(handler: (event: { id: string }) => void): () => void`
208
209Menu clicks arrive here. Without it there is no way to respond to a menu at all.
210Returns an unsubscribe function.
211
212```javascript
213const off = window.craft.menu.onAction(({ id }) => {
214 if (id === 'new') createDocument();
215 if (id === 'open') openDocument();
216});
217```
218
219Only items with an `id` fire this. Role items are performed by the system and
220never reach your handler.
221
222### Items and roles
223
224```typescript
225interface MenuDefinition {
226 label: string
227 items: MenuItemDefinition[]
228}
229
230interface MenuItemDefinition {
231 /** Echoed back by `onAction` when this item is clicked. */
232 id?: string
233 label?: string
234 /** Accelerator, e.g. `cmd+n`, `cmd+shift+r`. */
235 shortcut?: string
236 /** Icon name (SF Symbol, resolved through Craft's cross-platform icon table). */
237 icon?: string
238 /** Renders a divider; every other field is ignored when this is set. */
239 separator?: boolean
240 /** A standard behavior the system performs — see the table below. */
241 role?: string
242}
243```
244
245This is the whole of it. The native decoder parses exactly these fields and
246ignores anything else, so extra keys are dropped in silence rather than
247rejected. Two consequences worth knowing:
248
249- a separator is `{ separator: true }`, not `{ type: 'separator' }`
250- there are no nested submenus yet; the bar is one level deep
251
252Checkmarks and enablement are set after the fact, with `checkItem()` /
253`enableItem()`, not with a field here.
254
255**Roles.** A role item is wired to an AppKit selector through the responder
256chain, exactly as a hand-built Mac menu would be. Use one wherever the system
257already knows how to do the job:
258
259| role | performs | role | performs |
260|---|---|---|---|
261| `about` | About panel | `selectAll` | select all |
262| `hide` | hide app | `close` | close window |
263| `hideOthers` | hide other apps | `minimize` | minimize |
264| `showAll` | unhide all | `zoom` | zoom |
265| `quit` | quit | `front` | bring all to front |
266| `undo` / `redo` | undo, redo | `fullscreen` | toggle full screen |
267| `cut` / `copy` / `paste` | clipboard | `reload` | reload the webview |
268| `delete` | delete selection | `forceReload` | reload ignoring cache |
269
270Cut, copy and paste **must** be roles. A round trip through JavaScript cannot
271reach the field editor inside a text field or the WKWebView's own clipboard
272handling, so an `id` item named "Copy" will not copy anything.
273
274An unrecognized role degrades to an ordinary `id` item, which fires `onAction` —
275a newer bridge surface therefore fails soft on an older binary.
276
277### The rest of the surface
278
279| method | effect |
280|---|---|
281| `setDock({ items })` | replace the Dock menu (right-click the Dock icon) |
282| `clearDock()` | remove it |
283| `addItem(menuId, item)` | append one item to an existing menu; `menuId` is the menu's **label**, e.g. `'File'`. Pass `index` on the item to insert instead of append |
284| `removeItem(itemId)` | remove by id |
285| `enableItem(itemId)` / `disableItem(itemId)` | greyed out or not |
286| `checkItem(itemId)` / `uncheckItem(itemId)` | checkmark |
287| `setItemLabel(itemId, label)` | rename in place |
288
160289## Window API
161290
162291Control the application window from JavaScript.