Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
34 changes: 29 additions & 5 deletions docs/developer/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,16 +158,40 @@ failed renderer build leaves the displayed renderer and last published assets
untouched, and the next valid edit retries normally.

An edit used by Electron's main process or local backend prints the message
`restart required` and leaves the running window untouched. Stop and restart the loop
when that change should enter the application; the loop never turns a runtime
edit into an automatic app restart. Watcher implementation updates hand the
running app to the replacement watcher without quitting it. Generated output,
dependency directories, Git metadata, and instance state do not trigger
`restart required` and leaves the running window untouched. Stop and restart
the loop when that change should enter the application; the loop never turns a
runtime edit into an automatic app restart. Watcher implementation updates hand
the running app to the replacement watcher without quitting it. Generated
output, dependency directories, Git metadata, and instance state do not trigger
rebuilds. Keep only one loop per instance and use `npm start` for deterministic
one-shot work. End the loop with Ctrl-C; it asks the addressed instance to quit
through the managed durability path and waits for that process before
returning.

### Shared development element callouts

While the live development loop is running, Option-click any rendered element.
Markover pins a bright bounding box to that element and copies one opaque
`mko-ui-v1:` reference to the clipboard. Paste that reference into the agent
thread; no screenshot, DevTools inspection, or hand-drawn circle is needed.

An agent highlights the same element in the addressed running instance with:

```sh
npm --silent run markover -- --instance dev element highlight '<mko-ui-v1-reference>'
```

Clear the pinned box with:

```sh
npm --silent run markover -- --instance dev element clear
```

References use a validated unique-ID anchor and deterministic element path.
Stale or ambiguous references fail instead of selecting a different element.
The picker and authenticated highlight route exist only in a running live
development watcher; release and non-watch instances do not expose them.

## Development review links

Install or inspect the forwarding-only handler for the current worktree's open
Expand Down
1 change: 1 addition & 0 deletions scripts/app-layout.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const runtimeModuleNames = [
'codex-thread-titles',
'development-control',
'development-config',
'development-element',
'durability-shutdown',
'ipc-contract',
'ipc-security',
Expand Down
66 changes: 64 additions & 2 deletions scripts/markover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import {
import { serviceEndpointPath } from '../src/service-endpoint'
import { guidance } from '../src/agent-guidance'
import { normalizeSettings } from '../src/settings'
import { isDevelopmentElementCalloutResult } from '../src/development-element'
import { parseMarkdown } from '../src/tree'
import {
cleanupDevelopmentInstance,
Expand Down Expand Up @@ -110,6 +111,8 @@ export type ParsedCommand = ParsedInstanceTarget & (
| { command: 'canonical'; action: 'doctor' }
| { command: 'canonical'; action: 'refresh'; install: boolean }
| { command: 'cleanup'; expectedIdentity: `pr-${number}` }
| { command: 'element'; action: 'clear' }
| { command: 'element'; action: 'highlight'; reference: string }
| { command: 'edit'; reviewId: string }
| {
command: 'resolve'
Expand Down Expand Up @@ -309,6 +312,11 @@ export function helpPayload() {
usage: '--instance dev cleanup <pr-N>',
purpose: 'Move one stopped worktree-local instance to macOS Trash after its development URL handler has been removed.'
},
{
name: 'element',
usage: '--instance dev element highlight <mko-ui-v1-reference> | --instance dev element clear',
purpose: 'Highlight or clear one exact element callout in the addressed running live development window.'
},
{
name: 'help',
aliases: ['info', '--help', '-h'],
Expand Down Expand Up @@ -398,7 +406,8 @@ export function parseCommandArguments(args: string[]): ParsedCommand {
command !== 'pending' &&
command !== 'resolve' &&
command !== 'unresolve' &&
command !== 'cleanup'
command !== 'cleanup' &&
command !== 'element'
) {
if (command === 'check') {
throw commandError(
Expand All @@ -414,7 +423,34 @@ export function parseCommandArguments(args: string[]): ParsedCommand {
}
throw commandError(
`Unknown command: ${command}`,
'markover <open|get|get-for-review|submit|revise|done|edit|pending|resolve|unresolve|canonical|cleanup|help> ...'
'markover <open|get|get-for-review|submit|revise|done|edit|pending|resolve|unresolve|canonical|cleanup|element|help> ...'
)
}

if (command === 'element') {
if (instance !== 'development') {
throw commandError(
'element callouts are available only for the current live development worktree.',
'markover --instance dev element <highlight|clear> ...'
)
Comment thread
lastobelus marked this conversation as resolved.
Outdated
}
if (rest.length === 1 && rest[0] === 'clear') {
return targeted({ command, action: 'clear' as const })
}
if (
rest.length === 2 &&
rest[0] === 'highlight' &&
rest[1]?.startsWith('mko-ui-v1:')
) {
return targeted({
command,
action: 'highlight' as const,
reference: rest[1]
})
}
throw commandError(
'element requires highlight with one copied reference, or clear.',
'markover --instance dev element highlight <mko-ui-v1-reference> | markover --instance dev element clear'
)
}

Expand Down Expand Up @@ -1152,6 +1188,7 @@ export async function ensureService({

export interface ExecuteCommandOptions {
endpointPath?: string
requestLocal?: typeof requestJson
ensure?: () => Promise<void>
resolveTarget?: (
selector: InstanceSelector,
Expand Down Expand Up @@ -1622,6 +1659,31 @@ export async function executeCommand(
const cleanup = options.cleanup || cleanupDevelopmentInstance
return cleanup(instance, parsed.expectedIdentity)
}
if (parsed.command === 'element') {
const endpointPath = options.endpointPath || (
await resolveTarget('development')
).service.endpointPath
const result = await (options.requestLocal || requestJson)(
endpointPath,
'POST',
'/development/element-callout',
parsed.action === 'clear'
? { action: 'clear' }
: { action: 'highlight', reference: parsed.reference }
)
if (!isDevelopmentElementCalloutResult(result)) {
throw new LocalServiceError(
'INVALID_RESPONSE',
'Markover returned an invalid development element callout response.',
200
)
}
return {
...(result.bounds ? { bounds: result.bounds } : {}),
...(result.reference ? { reference: result.reference } : {}),
status: result.status
}
}

const profile = parsed.instance === undefined
? await (options.loadRemoteProfile || loadRemoteProfile)()
Expand Down
9 changes: 9 additions & 0 deletions src/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import type {
StartupPhaseEvent,
StartupReady
} from './startup-contract'
import type {
DevelopmentElementCalloutCommand,
DevelopmentElementCalloutResult
} from './development-element'

export interface DiffStats {
additions: number
Expand Down Expand Up @@ -754,6 +758,11 @@ declare global {
createLocalReview: (tree: ReviewTree) => Promise<MarkoverDocument>
onOpenMarkdownRequested: (callback: () => void) => void
onReviewBatchModeRequested: (callback: () => void) => void
onDevelopmentElementCallout: (
callback: (
command: DevelopmentElementCalloutCommand
) => DevelopmentElementCalloutResult | Promise<DevelopmentElementCalloutResult>
) => void
checksum: (source: string) => Promise<string>
copyText: (text: string) => void
readClipboardImage: () => Promise<MarkoverClipboardImage | null>
Expand Down
Loading
Loading