feat: F5.3b the editor on a project of server F
?project=<uuid> opens the shared document over /sync and the room over /sim (RemoteSimHost: messages wait for the socket, a lost socket comes back with what it watched, F's refusal or parting ends it); instead of the editor, a word why when F refuses or lets go. F tells each connection its access at once. Who only reads sees everything and changes nothing: no palette or undo, a locked surface, the sheet's fields disabled (tabs stay), no starting or stopping; thawed, the document comes anew. The toolbar names who started the simulation. Refusals carry no message, so Hocuspocus writes nothing outside F's JSON log. A browser test runs three people against a real F. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,12 @@ export interface CanvasProps {
|
||||
* Java's simulation mode. The documentation mode: see above.
|
||||
*/
|
||||
readonly mode?: 'design' | 'simulation' | 'documentation';
|
||||
/**
|
||||
* Who only reads (on F): a tap still chooses a device (its sheet shows
|
||||
* what it holds), but nothing moves, no cable is begun or chosen, no
|
||||
* documentation item is chosen or changed, and nothing has a menu.
|
||||
*/
|
||||
readonly locked?: boolean;
|
||||
/** A documentation item moved or resized: its new frame. */
|
||||
readonly onDocFrame?: (id: string, frame: Frame) => void;
|
||||
/** The field a focused text is typed into, over the surface at its frame. */
|
||||
@@ -228,6 +234,7 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
onMenu,
|
||||
highlightedLink = null,
|
||||
mode = 'design',
|
||||
locked = false,
|
||||
activeLinks = NO_LINKS,
|
||||
onActivate,
|
||||
onDocFrame,
|
||||
@@ -364,6 +371,10 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
if (pointers.current.size > 2) return;
|
||||
const target = event.target as Element;
|
||||
const linkId = target.closest('[data-link-hit]')?.getAttribute('data-link-hit');
|
||||
if (documenting && locked) {
|
||||
gesture.current = { kind: 'background', start: point, moved: false, last: point };
|
||||
return;
|
||||
}
|
||||
if (documenting) {
|
||||
const found = docAt(point);
|
||||
if (found === null) {
|
||||
@@ -391,11 +402,11 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
gesture.current = { kind: 'device', id: device.id, start: point, origin: { x: box.x, y: box.y }, side: device.side, moved: false, longPressed: false };
|
||||
longPress.current = setTimeout(() => {
|
||||
const now = gesture.current;
|
||||
if (now?.kind !== 'device' || now.moved) return;
|
||||
if (now?.kind !== 'device' || now.moved || (locked && !simulating)) return;
|
||||
now.longPressed = true;
|
||||
onMenu?.({ kind: 'device', id: now.id }, now.start);
|
||||
}, LONG_PRESS_MILLISECONDS);
|
||||
} else if (linkId !== undefined && linkId !== null && !simulating) {
|
||||
} else if (linkId !== undefined && linkId !== null && !simulating && !locked) {
|
||||
gesture.current = { kind: 'link', id: linkId, start: point, moved: false, longPressed: false };
|
||||
longPress.current = setTimeout(() => {
|
||||
const now = gesture.current;
|
||||
@@ -454,7 +465,7 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
const step = { x: point.x - now.last.x, y: point.y - now.last.y };
|
||||
now.last = point;
|
||||
setViewport((before) => shifted(before ?? currentRef.current, step));
|
||||
} else if (now.kind === 'device' && !now.longPressed && !simulating) {
|
||||
} else if (now.kind === 'device' && !now.longPressed && !simulating && !locked) {
|
||||
if (tool === 'cable') {
|
||||
const from = boxes.get(now.id)!.center;
|
||||
const target = deviceAt(point);
|
||||
@@ -501,6 +512,10 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
if (!now.moved) onActivate?.(now.id);
|
||||
return;
|
||||
}
|
||||
if (locked) {
|
||||
if (!now.moved) onSelect?.({ kind: 'device', id: now.id });
|
||||
return;
|
||||
}
|
||||
if (tool === 'cable') {
|
||||
setBand(null);
|
||||
const from = { id: now.id, side: now.side };
|
||||
@@ -645,7 +660,7 @@ export const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas(
|
||||
const corner = toScreen(current, { x: item.x, y: item.y });
|
||||
return renderDocText(item, { left: corner.x, top: corner.y, width: item.width * current.scale, height: item.height * current.scale, scale: current.scale });
|
||||
})()}
|
||||
{menuButton === null || selection === null || simulating ? null : (
|
||||
{menuButton === null || selection === null || simulating || locked ? null : (
|
||||
<button
|
||||
type="button"
|
||||
className="device-menu-button"
|
||||
|
||||
Reference in New Issue
Block a user