Export template source from be30143a326d5d7d7b0cbabea5bc5ac0301ef5b9:quixos-instance/packages/web-studio-canvas-runtime

This commit is contained in:
Quixos Template Publisher
2000-01-01 00:00:00 +00:00
commit 4a539f6005
31 changed files with 447 additions and 0 deletions
+8
View File
@@ -0,0 +1,8 @@
node_modules/
dist/
src/gen/
.quixos/
result
.yarn/cache/
.yarn/install-state.gz
.yarn/unplugged/
+4
View File
@@ -0,0 +1,4 @@
{
"monorepoCommit": "be30143a326d5d7d7b0cbabea5bc5ac0301ef5b9",
"sourcePath": "quixos-instance/packages/web-studio-canvas-runtime"
}
File diff suppressed because one or more lines are too long
+24
View File
@@ -0,0 +1,24 @@
enableScripts: true
generateDefaultNix: false
individualNixPackaging: true
nodeLinker: node-modules
supportedArchitectures:
os:
- current
- linux
cpu:
- current
- x64
- arm64
libc:
- current
- glibc
plugins:
- checksum: 271de9a785321a37c564b8e2a6b5cd689850b20572d5fee8ec0a6b8e7a5d4cfb59e533524af2b4a537e00da12828abd062eaec6978556e3dd9149a020db2591b
path: .yarn/plugins/yarn-plugin-nixify.cjs
spec: "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js"
+43
View File
@@ -0,0 +1,43 @@
# Studio graph capabilities
This system package implements ordinary Canvas/Placement/Placeable interfaces.
It owns no database, web server, Worker, page session, or special presentation
object per rendered subject. The workspace binds storage and relationship edges.
The checked component is an independent consumer of the same public contracts.
- `Canvas.create` creates an initialized canvas. The server entry factory uses the
workspace host's idempotent root protocol, without a second package-local cache.
- `Canvas.place` validates the requested renderer through the subject's portable
`Placeable.canPresent` contract and creates one placement with both relationships.
- `Canvas.createAndPlace` is an example compound factory: child canvas creation,
renderer validation and placement creation share one transaction. An invalid
renderer rolls back the child, too. Other domains can compose their own creation
ports in exactly this way; there is no frontend business reducer.
- `position` and `size` are separate whole-record registers. Concurrent move and
resize survive independently; concurrent moves resolve in server acceptance
order without mixing one move's x coordinate with another move's y coordinate.
- `remove` is explicitly server-only. Deletion needs the full incoming-reference
graph; a partial replica cannot safely predict all deletion restrictions and
cascades. Removing a placement preserves its subject. The interface does not
advertise a portable delete or keep a hidden archive in the canvas query.
The generic `canPresent<atom Subject>` implementation reads the explicitly granted
`reflection catalog [presentations]` port. This exposes only immutable checked
presentation IDs, subject contracts and their conforming atoms. It does not expose
live object inventory or private state. The dispatcher verifies the concrete
receiver; reference expectations supplied by a caller are not conformance proof.
A changed presentation catalog participates in the consuming execution group's
reuse digest. Unrelated modules do not acquire that dependency.
Queries have explicit prototype bounds: 100 canvases in navigation and 200
placements per open canvas. Title-only cards use their own small input projection.
A selected/new placement can retain `PlacementInputs` even if a list excludes it.
A larger Studio should add explicit windows or pagination rather than silently
pretend these bounded queries cover an arbitrarily large workspace.
`tools/local-first/studio.nix` builds the source snapshot through the checked
artifact builder and tests the generic workspace host. Run
`nix build .#checks.x86_64-linux.camino-studio-capabilities` at the monorepo root.
These are unpublished fixture resource pins; normal independently retained source
assembly and scaffolding are qualified in WI-12. The old checkedServer/Yarn and
migration recipes were removed, rather than presenting them as a compatible path.
+6
View File
@@ -0,0 +1,6 @@
{
"component:studio:canvas-card" = {
entry = "src/canvas-card.tsx";
export = "CanvasCard";
};
}
Generated
+108
View File
@@ -0,0 +1,108 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"helpers": {
"flake": false,
"locked": {
"lastModified": 1790852599,
"narHash": "sha256-MdNtZpcsoguLPSCpAy9Naz6fkVPrftbZQbz1Aj7Zm9A=",
"ref": "refs/tags/quixos-reachability/91679e8cc82133093cb0b4f9e519cbb39a5bba4f",
"rev": "91679e8cc82133093cb0b4f9e519cbb39a5bba4f",
"revCount": 54,
"type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
},
"original": {
"ref": "refs/tags/quixos-reachability/91679e8cc82133093cb0b4f9e519cbb39a5bba4f",
"rev": "91679e8cc82133093cb0b4f9e519cbb39a5bba4f",
"type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1783776592,
"narHash": "sha256-UgCQzxeWI75XM8G+hPrPh+MKzEPjG3SpAj7dtqSbksA=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "e7a3ca8092b61ff85b6a45bf863ea2b2d6a661b3",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"protocol": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
},
"locked": {
"lastModified": 1791261504,
"narHash": "sha256-QSk9DWR7n5KL6cMHzmSKKXNIyaFA0s8Nu+wXjhhlENI=",
"ref": "refs/tags/quixos-reachability/5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87",
"rev": "5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87",
"revCount": 184,
"type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
},
"original": {
"ref": "refs/tags/quixos-reachability/5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87",
"rev": "5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87",
"type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git"
}
},
"root": {
"inputs": {
"flake-utils": [
"protocol",
"flake-utils"
],
"helpers": "helpers",
"nixpkgs": [
"protocol",
"nixpkgs"
],
"protocol": "protocol"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}
+55
View File
@@ -0,0 +1,55 @@
{
inputs.protocol.url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-protocol.git?ref=refs/tags/quixos-reachability/5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87&rev=5b44c8154dc038efd87cf1cbdd2c00b66bfd4f87";
inputs.nixpkgs.follows = "protocol/nixpkgs";
inputs.flake-utils.follows = "protocol/flake-utils";
inputs.helpers = {
url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/tags/quixos-reachability/91679e8cc82133093cb0b4f9e519cbb39a5bba4f&rev=91679e8cc82133093cb0b4f9e519cbb39a5bba4f";
flake = false;
};
outputs =
{
nixpkgs,
flake-utils,
helpers,
...
}:
flake-utils.lib.eachDefaultSystem (
system:
let
pkgs = import nixpkgs { inherit system; };
camino = import (helpers + "/quixos-package-helpers.nix");
in
{
quixosPackages.checkedArtifacts =
context:
let
pkgs = camino.packageSet { inherit nixpkgs system context; };
dependencies =
if builtins.pathExists ./yarn-project.nix then
camino.mkCaminoYarnDependencies {
inherit pkgs;
src = ./.;
}
else
null;
in
camino.assembleCaminoPackage {
inherit pkgs context;
artifacts = [
(camino.mkCaminoTypeScriptPackage {
inherit pkgs context dependencies;
src = ./.;
entry = "src/server.ts";
entries.browser = "src/browser.ts";
})
(camino.mkCaminoReactPackage {
inherit pkgs context dependencies;
src = ./.;
modules = import ./components.nix;
})
];
};
devShells.default = camino.mkCaminoYarnShell { inherit pkgs; };
}
);
}
+5
View File
@@ -0,0 +1,5 @@
{
"name": "@quixos/web-studio-canvas-runtime",
"private": true,
"type": "module"
}
+45
View File
@@ -0,0 +1,45 @@
symbol JavaScript id "target:org.quixos.execution.javascript@1";
import interface WebStudioFactory;
import interface WebStudioSearchable;
import interface StandaloneView;
import interface WebStudioCanvas;
import interface WebStudioCanvasPlacement;
import interface WebStudioPlaceable;
external atom WebStudioCanvas id "atom:org.quixos.web-studio:canvas";
external atom WebStudioCanvasPlacement id "atom:org.quixos.web-studio:canvas-placement";
type Position=record {x:double;y:double;};
type Size=record {width:double;height:double;};
type Layout=record {x:double;y:double;width:double;height:double;z:int32;};
type PlacementSeed=record {canvas:interface-ref<WebStudioCanvas>;subject:interface-ref<WebStudioPlaceable>;presentation:string;position:Position;size:Size;z:int32;};
package WebStudioCanvasRuntime id "package:org.quixos.web-studio:canvas-runtime" revision "package:org.quixos.web-studio:canvas-runtime@1" {
function createPlaced<value Input, object Result implements WebStudioPlaceable> id "studio:create-placed" : record {canvas:interface-ref<WebStudioCanvas>;seed:Input;presentation:string;layout:Layout;} -> atom-ref<WebStudioCanvasPlacement> execution transaction targets [JavaScript,server] requires {
interface factory id "factory" : WebStudioFactory<Input,Result> using [create.call];
interface canvas id "canvas" : WebStudioCanvas using [place.call] from input;
};
function createCanvas id "studio:create-canvas" : string -> atom-ref<WebStudioCanvas> execution transaction targets [JavaScript,server] requires {creation canvas id "canvas" : WebStudioCanvas input record {name:string;};};
function createRoot id "studio:create-root" : unit -> atom-ref<WebStudioCanvas> execution transaction targets [server] requires {creation canvas id "canvas" : WebStudioCanvas input record {name:string;};};
operation canPresent<object Subject> id "studio:can-present" : string -> bool mode call execution snapshot targets [JavaScript,server] receiver object Subject requires {reflection catalog id "catalog" [presentations];};
operation place id "studio:place" : record {subject:interface-ref<WebStudioPlaceable>;presentation:string;layout:Layout;} -> atom-ref<WebStudioCanvasPlacement> mode call execution transaction targets [JavaScript,server] receiver any requires {
interface subject id "subject" : WebStudioPlaceable using [canPresent.call] from input;
creation placement id "placement" : WebStudioCanvasPlacement input PlacementSeed;
};
operation createAndPlace id "studio:create-and-place" : record {name:string;presentation:string;layout:Layout;} -> atom-ref<WebStudioCanvasPlacement> mode call execution transaction targets [JavaScript,server] receiver any requires {
interface subject id "subject" : WebStudioPlaceable using [canPresent.call] from input;
creation canvas id "canvas" : WebStudioCanvas input record {name:string;};
creation placement id "placement" : WebStudioCanvasPlacement input PlacementSeed;
};
module CanvasCard id "component:studio:canvas-card" implements StandaloneView<interface WebStudioCanvas>.render;
query TitleInputs id "studio:title-inputs" root WebStudioCanvas document "queries/title-inputs.graphql" operation "TitleInputs" {replica;}
query TitleView id "studio:title-view" root WebStudioCanvas document "queries/title-view.graphql" operation "TitleView" {local from TitleInputs;watch;}
query CanvasIndexInputs id "studio:canvas-index-inputs" root workspace WebStudioCanvas document "queries/canvas-index-inputs.graphql" operation "CanvasIndexInputs" {replica;}
query CanvasIndex id "studio:canvas-index" root workspace WebStudioCanvas document "queries/canvas-index.graphql" operation "CanvasIndex" {local from CanvasIndexInputs;watch;}
query PlaceableInputs id "studio:placeable-inputs" root WebStudioPlaceable document "queries/placeable-inputs.graphql" operation "PlaceableInputs" {replica;}
query PlaceableView id "studio:placeable-view" root WebStudioPlaceable document "queries/placeable-view.graphql" operation "PlaceableView" {local from PlaceableInputs;watch;}
query PlacementInputs id "studio:placement-inputs" root WebStudioCanvasPlacement document "queries/placement-inputs.graphql" operation "PlacementInputs" {replica;}
query CanvasInputs id "studio:canvas-inputs" root WebStudioCanvas document "queries/canvas-inputs.graphql" operation "CanvasInputs" {replica;max rows 200;}
query CanvasView id "studio:canvas-view" root WebStudioCanvas document "queries/canvas-view.graphql" operation "CanvasView" {local from CanvasInputs;max rows 200;watch;}
query SearchInputs id "studio:search-inputs" root workspace WebStudioSearchable document "queries/search-inputs.graphql" operation "SearchInputs" {replica;}
query Search id "studio:search" root workspace WebStudioSearchable document "queries/search.graphql" operation "Search" {local from SearchInputs;watch;}
query LabelInputs id "studio:label-inputs" root workspace WebStudioSearchable document "queries/label-inputs.graphql" operation "LabelInputs" {replica;}
query Labels id "studio:labels" root workspace WebStudioSearchable document "queries/labels.graphql" operation "Labels" {local from LabelInputs;watch;}
}
+1
View File
@@ -0,0 +1 @@
query CanvasIndexInputs { root { objects(all: 100) { entries { node { _camino {ref} name } } } } }
+1
View File
@@ -0,0 +1 @@
query CanvasIndex { root { availableObjects(all: 100) { entries { node { _camino {ref} name } } } } }
+1
View File
@@ -0,0 +1 @@
query CanvasInputs { root { name placements(all: 200) { entries { key node { _camino {ref} position {x y} size {width height} z presentation subject { _camino {ref} } } } } } }
+1
View File
@@ -0,0 +1 @@
query CanvasView { root { name placements(all: 200) { entries { key node { _camino {ref} position {x y} size {width height} z presentation subject { _camino {ref} } } } } } }
+1
View File
@@ -0,0 +1 @@
query LabelInputs($refs: [QxRef_WebStudioSearchable!]!) { root { objects(all: 100, where: {_camino: {ref: {in: $refs}}}) { entries { node { _camino {ref} title } } } } }
+1
View File
@@ -0,0 +1 @@
query Labels($refs: [QxRef_WebStudioSearchable!]!) { root { objects(all: 100, where: {_camino: {ref: {in: $refs}}}) { entries { node { _camino {ref} title } } } } }
+1
View File
@@ -0,0 +1 @@
query PlaceableInputs { root { _camino { ref } } }
+1
View File
@@ -0,0 +1 @@
query PlaceableView { root { _camino { ref } } }
+1
View File
@@ -0,0 +1 @@
query PlacementInputs { root { _camino {ref} position {x y} size {width height} z presentation subject { _camino {ref} } canvas { _camino {ref} name } } }
+1
View File
@@ -0,0 +1 @@
query SearchInputs($text: String!, $after: Cursor) { root { objects(first: 30, after: $after, where: {or: [{title: {containsInsensitive: $text}}, {searchText: {containsInsensitive: $text}}]}, orderBy: {title: ASC}) { entries { node { _camino {ref} title } } pageInfo {hasNextPage endCursor} } } }
+1
View File
@@ -0,0 +1 @@
query Search($text: String!, $after: Cursor) { root { objects(first: 30, after: $after, where: {or: [{title: {containsInsensitive: $text}}, {searchText: {containsInsensitive: $text}}]}, orderBy: {title: ASC}) { entries { node { _camino {ref} title } } pageInfo {hasNextPage endCursor} } } }
+1
View File
@@ -0,0 +1 @@
query TitleInputs { root { name } }
+1
View File
@@ -0,0 +1 @@
query TitleView { root { name } }
+4
View File
@@ -0,0 +1,4 @@
{
"backend": "execution",
"queryFrontends": ["react"]
}
+9
View File
@@ -0,0 +1,9 @@
quixos-lock version 1 {
quixos source { repository "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git"; commit "be30143a326d5d7d7b0cbabea5bc5ac0301ef5b9"; }
interface WebStudioFactory source { repository "https://repos.quixos.org/quixos-studio/interface-web-studio-factory.git"; commit "a4136d9bfd765db6b2de2188ef2caf341e0f7a61"; }
interface WebStudioSearchable source { repository "https://repos.quixos.org/quixos-studio/interface-web-studio-searchable.git"; commit "b51116e6d320fc4dcd79f0d067124adf6a8e919f"; }
interface StandaloneView source { repository "https://repos.quixos.org/quixos-studio/interface-react-standalone-view.git"; commit "67b00f7ff8df87a477e12c5668c42684d8fa1708"; }
interface WebStudioCanvas source { repository "https://repos.quixos.org/quixos-studio/interface-web-studio-canvas.git"; commit "fb076e2d0d48080f76b88b2ffa4edeb4c37a7342"; }
interface WebStudioCanvasPlacement source { repository "https://repos.quixos.org/quixos-studio/interface-web-studio-canvas-placement.git"; commit "8bee874e37700fc860f066f11946efc82a1f521d"; }
interface WebStudioPlaceable source { repository "https://repos.quixos.org/quixos-studio/interface-web-studio-placeable.git"; commit "a998ebd982f7b61946156a0fa7038bc1295c3afc"; }
}
+19
View File
@@ -0,0 +1,19 @@
{
"modules": {
"CanvasCard": {
"inputs": [
{
"name": "body",
"query": "TitleView",
"root": {
"from": "input",
"path": ["subject"]
},
"arguments": {
"record": {}
}
}
]
}
}
}
+3
View File
@@ -0,0 +1,3 @@
import { createRegistry } from "./gen/qx.js";
import { handlers } from "./portable.js";
export const registry = createRegistry(handlers);
+2
View File
@@ -0,0 +1,2 @@
import type { Modules } from "./gen/client.js";
export const CanvasCard: Modules["CanvasCard"] = ({ input }) => <p>{input.body.root.name}</p>;
+80
View File
@@ -0,0 +1,80 @@
import { QxReject, type ExactReference, type MutationKey } from "@quixos/camino-replica-core";
import { generic, type Contexts, type PortableImplementation } from "./gen/qx.js";
const position = (value: { readonly x: number; readonly y: number }) => {
if (!Number.isFinite(value.x) || !Number.isFinite(value.y)) throw new QxReject("INVALID_POSITION");
};
const size = (value: { readonly width: number; readonly height: number }) => {
if (!Number.isFinite(value.width) || !Number.isFinite(value.height) || value.width <= 0 || value.height <= 0)
throw new QxReject("INVALID_SIZE");
};
const canPresent = async ({ receiver, input, ports }: Contexts["canPresent"]) => {
const presentations = await ports.catalog.presentations();
return presentations.some((p) => p.id === input && p.atoms.includes(receiver.expectation.id));
};
const place = async ({ receiver, input, ports }: Contexts["place"]) => {
position(input.layout);
size(input.layout);
if (!(await ports.subject["canPresent.call"]({ receiver: input.subject, input: input.presentation })))
throw new QxReject("INVALID_PRESENTATION");
return ports.placement.create(
{
canvas: { ...receiver, expectation: { kind: "interface", id: "interface:org.quixos.web-studio.canvas@1" } },
subject: input.subject,
presentation: input.presentation,
position: { x: input.layout.x, y: input.layout.y },
size: { width: input.layout.width, height: input.layout.height },
z: input.layout.z,
},
{ key: "placement" },
);
};
export const createPlaced = async <Input>({
input,
ports,
}: {
input: {
seed: Input;
canvas: Contexts["place"]["receiver"];
presentation: string;
layout: Contexts["place"]["input"]["layout"];
};
ports: {
factory: { "create.call"(input: Input, key: MutationKey): Promise<ExactReference> };
canvas: {
"place.call"(
input: { receiver: ExactReference; input: Contexts["place"]["input"] },
key: MutationKey,
): Promise<Awaited<ReturnType<PortableImplementation["place"]>>>;
};
};
}) => {
const subject = await ports.factory["create.call"](input.seed, { key: "subject" });
return ports.canvas["place.call"](
{
receiver: input.canvas,
input: {
subject: { ...subject, expectation: { kind: "interface", id: "interface:org.quixos.web-studio.placeable@1" } },
presentation: input.presentation,
layout: input.layout,
},
},
{ key: "placement" },
);
};
export const handlers: Omit<PortableImplementation, "createRoot"> = {
...generic.canPresent(canPresent),
...generic.createPlaced(createPlaced),
createCanvas: ({ input, ports }) => ports.canvas.create({ name: input }, { key: "canvas" }),
place,
createAndPlace: async (context) => {
const subject = await context.ports.canvas.create({ name: context.input.name }, { key: "canvas" });
return place({
...context,
input: {
subject: { ...subject, expectation: { kind: "interface", id: "interface:org.quixos.web-studio.placeable@1" } },
presentation: context.input.presentation,
layout: context.input.layout,
},
});
},
};
+7
View File
@@ -0,0 +1,7 @@
import { createRegistry } from "./gen/qx.js";
import { handlers } from "./portable.js";
// The workspace entry protocol owns stable birth identity and idempotency.
export const registry = createRegistry({
...handlers,
createRoot: ({ ports }) => ports.canvas.create({ name: "Workspace" }, { key: "canvas" }),
});
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"strict": true,
"target": "ES2024",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"jsx": "react-jsx",
"noEmit": true
},
"include": ["src/**/*.ts", "src/**/*.tsx"]
}