Compare commits

..
29 changed files with 380 additions and 1386 deletions
-4
View File
@@ -1,4 +0,0 @@
{
"monorepoCommit": "4c88728f144add3457b4273d4b9a04ee53c53be3",
"sourcePath": "quixos-instance/packages/todo-react-runtime"
}
+4 -5
View File
@@ -1,9 +1,8 @@
# Generated from the checked package contract
package_id: "package:todo-react-runtime" package_id: "package:todo-react-runtime"
package_revision_id: "package:todo-react-runtime@1" package_revision_id: "package:todo-react-runtime@1"
runtime_protocol_version: "quixos-capabilities-v1" runtime_protocol_version: "quixos-capabilities-v1"
exports: { export_id: "export:todo-react-runtime:create-component" runtime_symbol: "createComponent" } exports: { export_id: "export:todo-react-runtime:create-component" runtime_symbol: "createComponent" }
exports: { export_id: "export:todo-react-runtime:source-get" runtime_symbol: "sourceGet" } exports: { export_id: "export:todo-react-runtime:source-get" runtime_symbol: "source.get" }
exports: { export_id: "export:todo-react-runtime:props-get" runtime_symbol: "propsGet" } exports: { export_id: "export:todo-react-runtime:props-get" runtime_symbol: "props.get" }
exports: { export_id: "export:todo-react-runtime:props-watch" runtime_symbol: "propsWatch" } exports: { export_id: "export:todo-react-runtime:props-watch" runtime_symbol: "props.watch" }
exports: { export_id: "export:todo-react-runtime:props-watch-stop" runtime_symbol: "propsWatchStop" } exports: { export_id: "export:todo-react-runtime:props-watch-stop" runtime_symbol: "props.watchStop" }
Generated
+7 -7
View File
@@ -37,17 +37,17 @@
"quixosNixHelpers": { "quixosNixHelpers": {
"flake": false, "flake": false,
"locked": { "locked": {
"lastModified": 1789602549, "lastModified": 1788543053,
"narHash": "sha256-DD66dQ3dzEvG13Zju/4sVSV4pnktSUkJ/Rbr1KkHMdg=", "narHash": "sha256-3YReATWEVxZinYVi/l3cwOz+oFoZUrwj24kUBQTmUpQ=",
"ref": "refs/tags/quixos-reachability/e2747066ca7d9d001dcb5dd936e17969ed88fc5f", "ref": "refs/tags/quixos-reachability/56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a",
"rev": "e2747066ca7d9d001dcb5dd936e17969ed88fc5f", "rev": "56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a",
"revCount": 32, "revCount": 8,
"type": "git", "type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git" "url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
}, },
"original": { "original": {
"ref": "refs/tags/quixos-reachability/e2747066ca7d9d001dcb5dd936e17969ed88fc5f", "ref": "refs/tags/quixos-reachability/56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a",
"rev": "e2747066ca7d9d001dcb5dd936e17969ed88fc5f", "rev": "56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a",
"type": "git", "type": "git",
"url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git" "url": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git"
} }
+9 -11
View File
@@ -6,33 +6,31 @@
flake-utils.url = "github:numtide/flake-utils"; flake-utils.url = "github:numtide/flake-utils";
quixosNixHelpers = { quixosNixHelpers = {
url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/tags/quixos-reachability/e2747066ca7d9d001dcb5dd936e17969ed88fc5f&rev=e2747066ca7d9d001dcb5dd936e17969ed88fc5f"; url = "git+https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos-nix-helpers.git?ref=refs/tags/quixos-reachability/56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a&rev=56bbcd22d17f8c7f120b1e0b5bd1d5a86e91a80a";
flake = false; flake = false;
}; };
}; };
outputs = outputs = inputs@{ nixpkgs, flake-utils, quixosNixHelpers, ... }:
inputs@{
nixpkgs,
flake-utils,
quixosNixHelpers,
...
}:
let let
quixosHelpers = import "${quixosNixHelpers}/quixos-package-helpers.nix"; quixosHelpers = import "${quixosNixHelpers}/quixos-package-helpers.nix";
in in
quixosHelpers.mkCaminoTsYarnNixifyFlake { quixosHelpers.mkCaminoTsYarnNixifyFlake {
inherit inputs nixpkgs flake-utils; inherit inputs nixpkgs flake-utils;
packageRoot = ./.; packageRoot = ./.;
migrationEntrypoint = "dist/migrate.js";
promptName = "todo-react-runtime"; promptName = "todo-react-runtime";
bundle = { bundle = {
entry = "src/server.ts"; entry = "dist/server.js";
browserSources = true;
}; };
installServer = { installServer = {
libexecName = "todo-react-runtime"; libexecName = "todo-react-runtime";
descriptorPath = "descriptor.quixos-package.txtpb"; descriptorPath = "descriptor.quixos-package.txtpb";
extraFiles = [
{
source = "dist/todo-component.mjs";
target = "todo-component.mjs";
}
];
}; };
}; };
} }
+2 -4
View File
@@ -8,17 +8,15 @@
"server": "dist/server.js" "server": "dist/server.js"
}, },
"scripts": { "scripts": {
"build": "rm -rf dist && tsc -p tsconfig.build.json", "build": "rm -rf dist && tsc -p tsconfig.build.json && node scripts/build-component.mjs",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"dependencies": { "dependencies": {
"@quixos/camino-package-runtime": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=446fd4e36e9700cf27ce20b472a8d512133e9370" "@quixos/camino-package-runtime": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=a998690446ecf647e3af78e369492c1a3663b66d"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "^24", "@types/node": "^24",
"@types/react": "^18.3.12",
"esbuild": "^0.25.12", "esbuild": "^0.25.12",
"react": "^18.3.1",
"typescript": "^7.0.2" "typescript": "^7.0.2"
} }
} }
+16 -15
View File
@@ -1,25 +1,26 @@
import interface TagData;
import interface CompletionData;
import interface TaskActions;
external atom Tag id "atom:tag";
external atom Completion id "atom:completion";
import interface TaskData;
external atom TaskReactComponent id "atom:task-react-component"; external atom TaskReactComponent id "atom:task-react-component";
external atom Task id "atom:task"; external atom Task id "atom:task";
type TaskProps = record {data: interface-ref<TaskData>;};
package TodoReactRuntime id "package:todo-react-runtime" revision "package:todo-react-runtime@1" { package TodoReactRuntime id "package:todo-react-runtime" revision "package:todo-react-runtime@1" {
constructor createComponent id "export:todo-react-runtime:create-component" constructs TaskReactComponent : atom-ref<Task>; constructor createComponent id "export:todo-react-runtime:create-component" constructs TaskReactComponent : atom-ref<Task>;
operation sourceGet id "export:todo-react-runtime:source-get" : unit -> string mode call receiver atom TaskReactComponent; operation sourceGet id "export:todo-react-runtime:source-get" : unit -> string mode call receiver atom TaskReactComponent;
operation propsGet id "export:todo-react-runtime:props-get" : unit -> TaskProps mode call receiver atom TaskReactComponent requires { operation propsGet id "export:todo-react-runtime:props-get" : unit -> message "org.quixos.web-studio.ReactProps" mode call receiver atom TaskReactComponent requires {
interface data id "port:todo-react-runtime:data" : TaskData; edge subject id "port:todo-react:subject" : exactly-one atom Task [resolve];
state title id "port:todo-react:title" : string [read];
state status id "port:todo-react:status" : string [read];
state priority id "port:todo-react:priority" : string [read];
state notes id "port:todo-react:notes" : string [read];
state dueDate id "port:todo-react:due-date" : string [read];
state energy id "port:todo-react:energy" : string [read];
}; };
operation propsWatch id "export:todo-react-runtime:props-watch" : unit -> watch-handle mode watch-start emits TaskProps receiver atom TaskReactComponent requires { operation propsWatch id "export:todo-react-runtime:props-watch" : unit -> watch-handle mode watch-start emits message "org.quixos.web-studio.ReactProps" receiver atom TaskReactComponent requires {
interface data id "port:todo-react-runtime:data" : TaskData; edge subject id "port:todo-react:subject" : exactly-one atom Task [resolve];
state title id "port:todo-react:title" : string [read];
state status id "port:todo-react:status" : string [read];
state priority id "port:todo-react:priority" : string [read];
state notes id "port:todo-react:notes" : string [read];
state dueDate id "port:todo-react:due-date" : string [read];
state energy id "port:todo-react:energy" : string [read];
}; };
operation propsWatchStop id "export:todo-react-runtime:props-watch-stop" : watch-handle -> unit mode watch-stop receiver atom TaskReactComponent; operation propsWatchStop id "export:todo-react-runtime:props-watch-stop" : watch-handle -> unit mode watch-stop receiver atom TaskReactComponent;
query History id "query:History" root TaskData document "queries/History.graphql" operation "History" {
view Tag as TagData; view Completion as CompletionData;
max rows 100; max depth 8; max result-bytes 1048576; watch;
}
} }
-5
View File
@@ -1,5 +0,0 @@
query History($first: Int!, $after: Cursor) {root {
tags(all: 100) {entries {node {_qx {ref} name color}}}
completions(first: $first, after: $after, orderBy: [{completedAt: DESC}]) {entries {key node {completedAt scheduledDate}} pageInfo {hasNextPage endCursor}}
_qx {relations {completions {aggregate {count}}}}
}}
-10
View File
@@ -1,10 +0,0 @@
{
"backend": "typescript",
"bindingOutput": "src/gen/qx.ts",
"options": {
"react": {
"propsExports": ["propsGet"],
"components": [{ "module": "../todo-component.js", "propsExport": "propsGet" }]
}
}
}
+1 -17
View File
@@ -1,22 +1,6 @@
quixos-lock version 1 { quixos-lock version 1 {
quixos source { quixos source {
repository "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git"; repository "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/quixos.git";
commit "4c88728f144add3457b4273d4b9a04ee53c53be3"; commit "9c3ab572ecfbc83785038c3d9f174516df15d63f";
}
interface TaskData source {
repository "https://repos.quixos.org/quixos-templates/interface-task-data.git";
commit "73d0b65861ab7d4194e431a6a38e28a288687fe9";
}
interface TagData source {
repository "https://repos.quixos.org/quixos-templates/interface-tag-data.git";
commit "944d1186ff82c2b00fe0db4fd839bbbfc66b0a72";
}
interface CompletionData source {
repository "https://repos.quixos.org/quixos-templates/interface-completion-data.git";
commit "6af9cc41f203378dce92f14b716708ba053d30fc";
}
interface TaskActions source {
repository "https://repos.quixos.org/quixos-templates/interface-task-actions.git";
commit "75c1f0cc6d201d34adf5d75695f7bab687157500";
} }
} }
-6
View File
@@ -1,6 +0,0 @@
{
"generatedBy": "qx-scaffold-v1",
"schemaVersion": 1,
"contracts": {},
"migrations": []
}
-4
View File
@@ -1,4 +0,0 @@
{
"generatedBy": "qx-scaffold-v1",
"nixifyPluginUrl": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/yarn-plugin-nixify-patched/raw/commit/4528fdd20b30d869262443b3f044549810e75fb8/dist/yarn-plugin-nixify.js"
}
+34
View File
@@ -0,0 +1,34 @@
import { build } from "esbuild";
const platformModules = new Map([
[
"@quixos/web-studio-react-runtime",
"/__quixos/platform/web-studio-react-runtime/v1.mjs",
],
["react", "/__quixos/platform/react/v18.mjs"],
["react/jsx-runtime", "/__quixos/platform/react-jsx-runtime/v18.mjs"],
["react/jsx-dev-runtime", "/__quixos/platform/react-jsx-dev-runtime/v18.mjs"],
]);
await build({
entryPoints: ["dist/todo-component.js"],
outfile: "dist/todo-component.mjs",
bundle: true,
format: "esm",
platform: "browser",
target: "es2022",
sourcemap: true,
plugins: [
{
name: "quixos-platform-modules",
setup(buildApi) {
buildApi.onResolve({ filter: /.*/ }, (args) => {
const platformPath = platformModules.get(args.path);
return platformPath
? { path: platformPath, external: true }
: undefined;
});
},
},
],
});
-5
View File
@@ -1,5 +0,0 @@
declare module "*?browser-source" {
const source: string;
export default source;
}
declare module "*.css";
-803
View File
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+29 -34
View File
@@ -1,39 +1,28 @@
// Generated by the Quixos React platform contract. Do not edit.
declare module "@quixos/web-studio-react-runtime" { declare module "@quixos/web-studio-react-runtime" {
import type * as React from "react";
export function useComponentOverlayContainer(): HTMLElement;
export function useComponentStyleRoot(): ShadowRoot;
export type ObjectRef<AtomId extends string> = string & { export type ObjectRef<AtomId extends string> = string & {
readonly $quixosAtom: AtomId; readonly $quixosAtom: AtomId;
}; };
export type FieldCapability = {objectId: string; interfaceRevisionId: string; getOperationId: string; watchOperationId?: string; setOperationId?: string; inputKind?: "fields" | "value"}; export type LiveFieldProp<T> = {
export type ReadableField<T> = {readonly value?: T; readonly capability: FieldCapability}; value: T;
/** set(T) uses the resolved capability: native CRDT fields send incremental edits; source: {
* custom/manual setters receive semantic values. Storage provenance grants no authority. */ objectId: string;
export type WritableField<T> = ReadableField<T> & {readonly $writeType?: (value: T) => T; readonly writable: true; readonly capability: FieldCapability & {setOperationId: string}}; slotId: string;
export type LiveFieldProp<T> = ReadableField<T>; valueType?: string;
export type InterfaceReference<I extends string, Fields> = {readonly $quixosRef: string; readonly interfaceRevisionId: I; readonly fields: Fields}; storagePolicy?: string;
export type ReactInterfaceContract<View> = {readonly interfaceRevisionId: string; readonly $viewType?: (view: View) => View}; revision?: string | number | bigint;
export function defineReactInterfaceContract<View>(interfaceRevisionId: string, interfaces: unknown): ReactInterfaceContract<View>; crdtSnapshot?: {
export function tryConform<View>(object: string | {readonly $quixosRef: string}, contract: ReactInterfaceContract<View>, options?: {signal?: AbortSignal}): Promise<View | undefined>; type: string;
export type ConformanceResult<View> = {status: "loading"} | {status: "absent"} | {status: "available"; view: View} | {status: "error"; error: Error}; encoding: string;
export function useTryConform<View>(object: string | {readonly $quixosRef: string}, contract: ReactInterfaceContract<View>): ConformanceResult<View>; payload: string;
export type QueryValueType = {kind: "builtin" | "scalar"; name: string} | {kind: "record"; fields: Record<string, QueryValueType>} | {kind: "optional" | "list"; value: QueryValueType} | {kind: "object-ref"; expectation: {kind: "atom"; atomId: string} | {kind: "interface"; interfaceRevisionId: string}}; };
export type QueryReference<Contract extends string = string> = {readonly $quixosRef: string; readonly queryContract: Contract}; };
export interface QueryDescriptor<Variables, Result> {readonly id: string; readonly definitionDigest: string; readonly rootInterfaceRevisionId: string; readonly variables: QueryValueType; readonly output: QueryValueType; readonly watch: boolean; readonly $types?: (variables: Variables, result: Result) => [Variables, Result]} };
export type QueryPartial<T> = T extends QueryReference ? T : T extends readonly (infer Item)[] ? QueryPartial<Item>[] : T extends object ? {[Key in keyof T]?: QueryPartial<T[Key]>} : T;
export type QueryFieldState = {path: readonly (string | number)[]; status: "pending" | "error"; error?: string};
export type QueryState<T> = {refreshing: boolean; fields: QueryFieldState[]; runId?: string; sequence?: bigint; consistency?: string} & ({status: "loading"; data?: undefined; error?: undefined} | {status: "ready"; data: T; error?: undefined} | {status: "partial"; data: QueryPartial<T>; error?: undefined} | {status: "error"; data?: T | QueryPartial<T>; error: Error});
export function useQuery<Variables, Result>(descriptor: QueryDescriptor<Variables, Result>, options: {root: string | {readonly $quixosRef: string}; variables: Variables}): QueryState<Result> & {refresh(): void};
export interface QueryConnection<Row> {entries: {key: string; node: Row; cursor?: string | null}[]; pageInfo: {hasNextPage: boolean; endCursor?: string | null}}
export function CollectionView<Result, Row>(props: {result: QueryState<Result> & {refresh(): void}; connection(data: QueryPartial<Result>): QueryPartial<QueryConnection<Row>> | undefined; renderItem(row: QueryPartial<Row>, entryKey: string): React.ReactNode; onCreate?: () => void; onLoadMore?: (cursor: string | null) => void; onReset?: () => void; loadingMore?: boolean; empty?: React.ReactNode; prefetch?: boolean}): React.ReactElement;
export type ReactComponentHostProps<Action> = { export type ReactComponentHostProps<Action> = {
onAction?: (action: Action) => void; onAction?: (action: Action) => void;
fallback?: React.ReactNode; fallback?: unknown;
className?: string; className?: string;
style?: React.CSSProperties; style?: Record<string, string | number | undefined>;
/** Optional extra action. The host still displays and reports errors by default. */ onError?: (error: Error) => void;
extraErrorAction?: (error: Error) => void;
}; };
export type ReactComponentImplementationProps<CaminoProps, RenderProps, Action> = { export type ReactComponentImplementationProps<CaminoProps, RenderProps, Action> = {
camino: CaminoProps; camino: CaminoProps;
@@ -50,10 +39,16 @@ declare module "@quixos/web-studio-react-runtime" {
interfaceRevisionId: string, interfaceRevisionId: string,
operationId: string, operationId: string,
value?: unknown, value?: unknown,
options?: {clientMutationId?: string; signal?: AbortSignal},
) => Promise<Result>; ) => Promise<Result>;
export const h: typeof React.createElement; export const h: (...args: any[]) => any;
export function useLiveField<T>(field: ReadableField<T>, options: {write: (value: T) => Promise<void>}): readonly [T, (value: T) => Promise<void>]; export const useLiveField: <T>(
export function useLiveField<T>(field: WritableField<T>): readonly [T, (value: T) => Promise<void>]; field: LiveFieldProp<T>,
export function useLiveField<T>(field: ReadableField<T>): readonly [T]; options?: {
reconcileRegister?: (state: {
confirmed: T;
optimistic: T;
pending: boolean;
}) => T;
},
) => [T, (value: T) => void | Promise<void>];
} }
-2
View File
@@ -1,2 +0,0 @@
// This adapter keeps the implementation file hand-authored.
export { createComponent as handler } from "../todo-react.impl.js";
-2
View File
@@ -1,2 +0,0 @@
// This adapter keeps the implementation file hand-authored.
export { props as handler } from "../todo-react.impl.js";
-2
View File
@@ -1,2 +0,0 @@
// This adapter keeps the implementation file hand-authored.
export { props as handler } from "../todo-react.impl.js";
-2
View File
@@ -1,2 +0,0 @@
// This adapter keeps the implementation file hand-authored.
export { stopWatch as handler } from "../todo-react.impl.js";
-2
View File
@@ -1,2 +0,0 @@
// This adapter keeps the implementation file hand-authored.
export { source as handler } from "../todo-react.impl.js";
-3
View File
@@ -1,3 +0,0 @@
// Generated by qx-scaffold-v1
import { serveMigration } from "@quixos/camino-package-runtime";
await serveMigration({});
+12 -16
View File
@@ -1,17 +1,13 @@
// Generated by qx-scaffold-v1
import { servePackageRuntime } from "@quixos/camino-package-runtime"; import { servePackageRuntime } from "@quixos/camino-package-runtime";
import { createRuntime } from "./gen/qx.js"; import { createComponent, props, source, stopWatch } from "./todo-react.impl.js";
import { handler as impl0 } from "./impl/createComponent.js";
import { handler as impl1 } from "./impl/sourceGet.js"; servePackageRuntime({
import { handler as impl2 } from "./impl/propsGet.js"; packageRevisionId: "package:todo-react-runtime@1",
import { handler as impl3 } from "./impl/propsWatch.js"; exports: {
import { handler as impl4 } from "./impl/propsWatchStop.js"; "export:todo-react-runtime:create-component": createComponent,
servePackageRuntime( "export:todo-react-runtime:source-get": source,
createRuntime({ "export:todo-react-runtime:props-get": props,
createComponent: impl0, "export:todo-react-runtime:props-watch": props,
sourceGet: impl1, "export:todo-react-runtime:props-watch-stop": stopWatch,
propsGet: impl2, },
propsWatch: impl3, });
propsWatchStop: impl4,
}),
);
-127
View File
@@ -1,127 +0,0 @@
.task-editor {
box-sizing: border-box;
min-width: 0;
height: 100%;
overflow: auto;
padding: 20px;
background: #fff;
color: #27372d;
font:
14px/1.5 system-ui,
sans-serif;
}
.task-editor * {
box-sizing: border-box;
}
.task-editor label {
display: flex;
flex-direction: column;
gap: 4px;
font-size: 12px;
color: #63715f;
}
.task-editor input,
.task-editor select,
.task-editor textarea,
.task-editor button {
font: inherit;
color: #27372d;
border: 1px solid #d4dccf;
border-radius: 6px;
padding: 8px 10px;
background: #fff;
min-width: 0;
}
.task-editor input,
.task-editor select,
.task-editor textarea {
width: 100%;
}
.task-editor textarea {
resize: vertical;
min-height: 120px;
}
.task-title input {
font-size: 21px;
font-weight: 650;
}
.task-actions {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
margin: 12px 0;
}
.task-actions span,
.task-hint {
color: #6c7867;
font-size: 12px;
}
.task-editor button {
cursor: pointer;
}
.task-editor button:hover,
.task-editor button[aria-pressed="true"] {
background: #e9f1e1;
}
.task-editor button:disabled {
opacity: 0.5;
cursor: wait;
}
.task-editor :focus-visible {
outline: 2px solid #42765a;
outline-offset: 2px;
}
.task-fields {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
gap: 12px;
}
.task-editor h3 {
font-size: 13px;
font-weight: 650;
margin: 20px 0 8px;
}
.task-tags {
display: flex;
gap: 6px;
flex-wrap: wrap;
margin-bottom: 16px;
}
.task-tags button {
padding: 4px 8px;
font-size: 12px;
}
.task-tags i {
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 5px;
}
.task-history {
border-top: 1px solid #e3e8de;
margin-top: 20px;
}
.task-history ol {
list-style: none;
padding: 0;
}
.task-history li {
display: flex;
flex-direction: column;
gap: 3px;
padding: 10px 0;
border-bottom: 1px solid #edf0e8;
}
.task-history li span {
font-size: 12px;
color: #6c7867;
}
.task-error {
background: #fff1ec;
color: #923c2c;
padding: 10px;
margin-bottom: 12px;
overflow-wrap: anywhere;
}
+209 -205
View File
@@ -1,218 +1,222 @@
import { useState } from "react";
import { import {
tryConform, h,
useLiveField, useLiveField,
useQuery, type LiveFieldProp,
type ObjectRef,
type ReactComponentImplementationProps, type ReactComponentImplementationProps,
} from "@quixos/web-studio-react-runtime"; } from "@quixos/web-studio-react-runtime";
import { queries, reactInterfaces, type ReactResults } from "./gen/react-props.gen.js"; import type {
import "./task.css"; TodoReactComponentAction,
TodoReactComponentRenderProps,
} from "./gen/quixos.todo.TodoReactComponent.react.js";
type Props = ReactComponentImplementationProps< type TodoReactComponentCaminoProps = {
ReactResults["propsGet"], task_id: string;
{ tagChoices?: { id: string; name: string; color: string }[] }, title: LiveFieldProp<string>;
never status: LiveFieldProp<string>;
priority: LiveFieldProp<string>;
notes: LiveFieldProp<string>;
due_date: LiveFieldProp<string>;
energy: LiveFieldProp<string>;
display_label: string;
};
declare global {
namespace JSX {
interface IntrinsicElements {
div: Record<string, unknown>;
button: Record<string, unknown>;
input: Record<string, unknown>;
label: Record<string, unknown>;
option: Record<string, unknown>;
select: Record<string, unknown>;
span: Record<string, unknown>;
strong: Record<string, unknown>;
textarea: Record<string, unknown>;
}
}
}
const palette = {
ink: "#28362d",
muted: "#6f776e",
cream: "#fffdf5",
line: "#d9d0ba",
moss: "#5f7f63",
honey: "#d99b3d",
berry: "#a9544f",
};
const statusTone = (status: string) => status === "DONE"
? palette.moss
: status === "ARCHIVED" ? palette.muted : "#52749a";
const fieldLabelStyle = {
display: "grid",
gap: "4px",
color: palette.muted,
fontSize: "10px",
fontWeight: 750,
letterSpacing: "0.08em",
textTransform: "uppercase" as const,
};
const controlStyle = {
minWidth: 0,
width: "100%",
border: `1px solid ${palette.line}`,
borderRadius: "7px",
background: "#fff",
color: palette.ink,
fontSize: "11px",
padding: "6px 8px",
};
type TodoComponentImplementationProps = ReactComponentImplementationProps<
TodoReactComponentCaminoProps,
TodoReactComponentRenderProps,
TodoReactComponentAction
>; >;
export default function TodoComponent({ camino: props, render }: Props) { export default function TodoComponent({
const [title, setTitle] = useLiveField(props.data.fields.title); camino: props,
const [status] = useLiveField(props.data.fields.status); render,
const [priority, setPriority] = useLiveField(props.data.fields.priority); dispatch,
const [notes, setNotes] = useLiveField(props.data.fields.notes); }: TodoComponentImplementationProps) {
const [dueDate, setDueDate] = useLiveField(props.data.fields.dueDate); const [title, setTitle] = useLiveField(props.title);
const [energy, setEnergy] = useLiveField(props.data.fields.energy); const [status, setStatus] = useLiveField(props.status);
const [hours, setHours] = useLiveField(props.data.fields.estimatedHours); const [priority, setPriority] = useLiveField(props.priority);
const [repeatDays, setRepeatDays] = useLiveField(props.data.fields.repeatDays); const [notes, setNotes] = useLiveField(props.notes);
const [after, setAfter] = useState<string | null>(null); const [dueDate, setDueDate] = useLiveField(props.due_date);
const history = useQuery(queries.History, { root: props.data.$quixosRef, variables: { first: 50, after } }); const [energy, setEnergy] = useLiveField(props.energy);
const [busy, setBusy] = useState(false); const done = status === "DONE";
const [error, setError] = useState("");
// Live setters retain the platform's optimistic/CRDT behavior; do not drop keystrokes while an action is pending.
const save = (request: Promise<unknown>) => {
void request.catch((e) => setError(e instanceof Error ? e.message : String(e)));
};
const run = async (action: () => Promise<unknown>) => {
if (busy) return;
setBusy(true);
setError("");
try {
await action();
} catch (e) {
setError(e instanceof Error ? e.message : String(e));
} finally {
setBusy(false);
}
};
const action = (name: "complete.call" | "reopen.call" | "archive.call") =>
run(async () => {
const view = await tryConform(props.data.$quixosRef, reactInterfaces.TaskActions);
if (!view) throw new Error("Task actions are unavailable");
await view.call[name]();
setAfter(null);
});
const toggleTag = (id: string, connected: boolean) =>
run(async () => {
const view = await tryConform(props.data.$quixosRef, reactInterfaces.TaskData);
if (!view) throw new Error("Task is unavailable");
// These IDs originate in atom-Tag relations (the project catalog or this task), not user-entered strings.
await view.call[connected ? "tags.disconnect" : "tags.connect"](id as ObjectRef<"atom:tag">);
});
const data = history.status === "ready" ? history.data.root : undefined;
const assigned =
data?.tags.entries.map((e) => ({ id: e.node._qx.ref.$quixosRef, name: e.node.name, color: e.node.color })) ?? [];
const choices = [...assigned, ...(render.tagChoices ?? []).filter((t) => !assigned.some((a) => a.id === t.id))];
return ( return (
<article className="task-editor"> <div style={{
{error && ( display: "grid",
<div role="alert" className="task-error"> gap: render.compact ? "9px" : "13px",
{error} minWidth: 0,
height: "100%",
padding: render.compact ? "11px" : "15px",
border: render.selected ? `2px solid ${palette.honey}` : `1px solid ${palette.line}`,
borderRadius: "12px",
background: palette.cream,
boxShadow: render.selected ? "0 0 0 3px rgba(217,155,61,.14)" : "none",
color: palette.ink,
fontFamily: "Avenir Next, Nunito, ui-sans-serif, system-ui, sans-serif",
overflow: "auto",
}}>
<div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: "10px" }}>
<button
type="button"
aria-label={done ? "Reopen task" : "Complete task"}
onClick={() => void setStatus(done ? "OPEN" : "DONE")}
style={{
flex: "0 0 auto",
width: "26px",
height: "26px",
border: `1px solid ${done ? palette.moss : palette.line}`,
borderRadius: "8px",
background: done ? palette.moss : "#fff",
color: "#fff",
fontWeight: 900,
cursor: "pointer",
}}
>
{done ? "✓" : ""}
</button>
<div style={{ display: "grid", flex: 1, gap: "3px", minWidth: 0 }}>
<strong style={{
overflow: "hidden",
fontSize: render.compact ? "14px" : "17px",
lineHeight: 1.25,
textDecoration: done ? "line-through" : "none",
textOverflow: "ellipsis",
whiteSpace: "nowrap",
}}>
{title || "Untitled task"}
</strong>
<span style={{ color: statusTone(status), fontSize: "10px", fontWeight: 800, letterSpacing: ".08em" }}>
{status || "OPEN"} · {priority || "NORMAL"}
</span>
</div>
<button
type="button"
onClick={() => dispatch({ type: "SELECT" })}
style={{
border: "none",
background: "transparent",
color: palette.muted,
cursor: "pointer",
fontSize: "12px",
}}
>
{render.selected ? "Pinned" : "Open"}
</button>
</div>
{!render.compact ? (
<div style={{ display: "contents" }}>
<label style={fieldLabelStyle}>
Title
<input
value={title}
onInput={(event: { currentTarget: HTMLInputElement }) => void setTitle(event.currentTarget.value)}
style={controlStyle}
/>
</label>
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(112px, 1fr))", gap: "8px" }}>
<label style={fieldLabelStyle}>
Status
<select style={controlStyle} value={status} onChange={(event: { currentTarget: HTMLSelectElement }) => void setStatus(event.currentTarget.value)}>
<option value="OPEN">Open</option>
<option value="DONE">Done</option>
<option value="ARCHIVED">Archived</option>
</select>
</label>
<label style={fieldLabelStyle}>
Priority
<select style={controlStyle} value={priority} onChange={(event: { currentTarget: HTMLSelectElement }) => void setPriority(event.currentTarget.value)}>
<option value="LOW">Low</option>
<option value="NORMAL">Normal</option>
<option value="HIGH">High</option>
</select>
</label>
<label style={fieldLabelStyle}>
Energy
<select style={controlStyle} value={energy} onChange={(event: { currentTarget: HTMLSelectElement }) => void setEnergy(event.currentTarget.value)}>
<option value="LOW">Low</option>
<option value="MEDIUM">Medium</option>
<option value="HIGH">High</option>
</select>
</label>
<label style={fieldLabelStyle}>
Due
<input style={controlStyle} type="date" value={dueDate} onInput={(event: { currentTarget: HTMLInputElement }) => void setDueDate(event.currentTarget.value)} />
</label>
</div>
<label style={fieldLabelStyle}>
Notes
<textarea
aria-label="Notes"
value={notes}
onInput={(event: { currentTarget: HTMLTextAreaElement }) => void setNotes(event.currentTarget.value)}
rows={4}
placeholder="Leave a trail for your future self…"
style={{ ...controlStyle, minHeight: "78px", resize: "vertical" }}
/>
</label>
</div>
) : (
<div style={{ display: "flex", flexWrap: "wrap", gap: "6px", color: palette.muted, fontSize: "11px" }}>
{dueDate ? <span>Due {dueDate}</span> : <span>No due date</span>}
<span>·</span>
<span>{energy || "MEDIUM"} energy</span>
</div> </div>
)} )}
<label className="task-title">
Task title <div style={{ color: palette.muted, fontSize: "10px", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
<input value={title} onChange={(e) => save(setTitle(e.target.value))} /> {props.task_id}
</label>
<div className="task-actions">
{status === "OPEN" ? (
<button
disabled={busy}
onClick={() => {
void action("complete.call");
}}
>
{repeatDays > 0 ? "Record completion" : "Complete task"}
</button>
) : (
<button
disabled={busy}
onClick={() => {
void action("reopen.call");
}}
>
Reopen task
</button>
)}
{status !== "ARCHIVED" && (
<button
disabled={busy}
onClick={() => {
void action("archive.call");
}}
>
Archive
</button>
)}
<span>{status.toLowerCase()}</span>
</div> </div>
<div className="task-fields"> </div>
<label>
Priority
<select value={priority} onChange={(e) => save(setPriority(e.target.value))}>
<option>LOW</option>
<option>NORMAL</option>
<option>HIGH</option>
</select>
</label>
<label>
Energy
<select value={energy} onChange={(e) => save(setEnergy(e.target.value))}>
<option>LOW</option>
<option>MEDIUM</option>
<option>HIGH</option>
</select>
</label>
<label>
Due date
<input type="date" value={dueDate} onChange={(e) => save(setDueDate(e.target.value))} />
</label>
<label>
Estimated hours
<input
type="number"
min="0"
step="0.25"
value={hours}
onChange={(e) => {
const n = Number(e.target.value);
if (Number.isFinite(n) && n >= 0) save(setHours(n));
}}
/>
</label>
<label>
Repeat after (days)
<input
type="number"
min="0"
max="36500"
step="1"
value={repeatDays}
onChange={(e) => {
const n = Number(e.target.value);
if (Number.isInteger(n) && n >= 0 && n <= 36500) save(setRepeatDays(n));
}}
/>
</label>
</div>
<p className="task-hint">
{repeatDays > 0
? `After completion, stay open and set the next due date ${repeatDays} days later (UTC).`
: "One-off task. Set repeat days above zero to repeat after completion."}
</p>
<section>
<h3>Tags</h3>
<div className="task-tags">
{choices.map((t) => {
const connected = assigned.some((a) => a.id === t.id);
return (
<button
key={t.id}
disabled={busy || !data}
aria-pressed={connected}
onClick={() => {
void toggleTag(t.id, connected);
}}
>
<i style={{ background: t.color }} />
{t.name}
{connected ? " ✓" : ""}
</button>
);
})}
</div>
{choices.length === 0 && <p className="task-hint">Create and assign tags from the project's tag catalog.</p>}
</section>
<label>
Notes
<textarea value={notes} rows={8} onChange={(e) => save(setNotes(e.target.value))} />
</label>
<section className="task-history">
<h3>Completion history{data ? ` · ${String(data._qx.relations.completions.aggregate.count)}` : ""}</h3>
{history.status === "error" && (
<div role="alert">
{history.error.message}
<button onClick={() => setAfter(null)}>Return to latest</button>
</div>
)}
{history.status === "loading" && <p>Loading history…</p>}
{data?.completions.entries.length === 0 && <p className="task-hint">No completions yet.</p>}
<ol>
{data?.completions.entries.map(({ key, node }) => (
<li key={key}>
<time dateTime={new Date(node.completedAt).toISOString()}>
{new Date(node.completedAt).toLocaleString()}
</time>
<span>{node.scheduledDate ? `Scheduled for ${node.scheduledDate}` : "No scheduled date"}</span>
</li>
))}
</ol>
<div className="task-actions">
{after && <button onClick={() => setAfter(null)}>Latest</button>}
{data?.completions.pageInfo.hasNextPage && (
<button onClick={() => setAfter(data.completions.pageInfo.endCursor)}>Older completions →</button>
)}
</div>
</section>
</article>
); );
} }
+50 -8
View File
@@ -1,10 +1,52 @@
import componentSource from "./todo-component.js?browser-source"; import fs from "node:fs/promises";
import type { Contexts, Results } from "./gen/qx.js"; import path from "node:path";
import { fileURLToPath } from "node:url";
import { derived, type RuntimeContext } from "@quixos/camino-package-runtime";
const dirname = path.dirname(fileURLToPath(import.meta.url));
const componentSourcePath = path.join(dirname, "todo-component.mjs");
export const source = async () => await fs.readFile(componentSourcePath, "utf8");
export const createComponent = (context: RuntimeContext) => context.objectId;
export const props = derived(async (context: RuntimeContext) => {
const tasks = await context.edge("port:todo-react:subject").resolve();
if (tasks.length !== 1) {
throw new Error(`Task component ${context.objectId} has ${tasks.length} subjects`);
}
const titlePort = context.state<string>("port:todo-react:title");
const statusPort = context.state<string>("port:todo-react:status");
const priorityPort = context.state<string>("port:todo-react:priority");
const notesPort = context.state<string>("port:todo-react:notes");
const dueDatePort = context.state<string>("port:todo-react:due-date");
const energyPort = context.state<string>("port:todo-react:energy");
const [title, status, priority, titleLive, statusLive, priorityLive, notesLive, dueDateLive, energyLive] =
await Promise.all([
titlePort.get(),
statusPort.get(),
priorityPort.get(),
titlePort.live(),
statusPort.live(),
priorityPort.live(),
notesPort.live(),
dueDatePort.live(),
energyPort.live(),
]);
const priorityText = priority && priority !== "NORMAL"
? ` ${priority.toLowerCase()}`
: "";
return {
task_id: tasks[0],
component_id: context.objectId,
title: titleLive,
status: statusLive,
priority: priorityLive,
notes: notesLive,
due_date: dueDateLive,
energy: energyLive,
display_label: `[${status ?? "OPEN"}]${priorityText} ${title ?? "Untitled task"}`,
};
});
export const source = () => componentSource;
export const createComponent = ({ objectId }: Contexts["createComponent"]) => objectId;
export const props = {
kind: "derived" as const,
get: async ({ ports }: Contexts["propsGet"]): Promise<Results["propsGet"]> => ({ data: ports.data.objectId }),
};
export const stopWatch = () => null; export const stopWatch = () => null;
+2 -1
View File
@@ -12,7 +12,8 @@
"types": ["node"], "types": ["node"],
"outDir": "dist", "outDir": "dist",
"rootDir": "src", "rootDir": "src",
"jsx": "react-jsx" "jsx": "react",
"jsxFactory": "h"
}, },
"include": ["src/**/*.ts", "src/**/*.tsx"] "include": ["src/**/*.ts", "src/**/*.tsx"]
} }
+1 -7
View File
@@ -161,17 +161,11 @@ cacheEntries = {
"@connectrpc/connect@npm:2.1.2" = { filename = "@connectrpc-connect-npm-2.1.2-ab34825de3-4e15000890.zip"; hash = "sha512-ThUACJAjt1Lv1E+pQKWuHh+gDhvrxLoM7ScruTWSDYw7hbd0UOZErUQruMqaJ1sMFxNviWuo7/A2F4Ex2wHWFQ=="; }; "@connectrpc/connect@npm:2.1.2" = { filename = "@connectrpc-connect-npm-2.1.2-ab34825de3-4e15000890.zip"; hash = "sha512-ThUACJAjt1Lv1E+pQKWuHh+gDhvrxLoM7ScruTWSDYw7hbd0UOZErUQruMqaJ1sMFxNviWuo7/A2F4Ex2wHWFQ=="; };
"@esbuild/linux-arm64@npm:0.25.12" = { filename = "@esbuild-linux-arm64-npm-0.25.12-a5aaa0e70e-10c0.zip"; hash = "sha512-tm9zhnNR4GZv4A+Sgbt9jdCJ0iGlzvYxGCaxaScbhFH14mX7QWJRYaCvFCZB1dseApmaq6mNwbilL0wJnbB8tw=="; }; "@esbuild/linux-arm64@npm:0.25.12" = { filename = "@esbuild-linux-arm64-npm-0.25.12-a5aaa0e70e-10c0.zip"; hash = "sha512-tm9zhnNR4GZv4A+Sgbt9jdCJ0iGlzvYxGCaxaScbhFH14mX7QWJRYaCvFCZB1dseApmaq6mNwbilL0wJnbB8tw=="; };
"@esbuild/linux-x64@npm:0.25.12" = { filename = "@esbuild-linux-x64-npm-0.25.12-07c7c22cce-10c0.zip"; hash = "sha512-U2QGT8ZIZN8q2WPSFDC32+sZGKgp49gvnHzIf/vKVR4xIzjcgBbjAIKI+FqfGBmGBWu8eCpS7lR9JDXdUHOFkw=="; }; "@esbuild/linux-x64@npm:0.25.12" = { filename = "@esbuild-linux-x64-npm-0.25.12-07c7c22cce-10c0.zip"; hash = "sha512-U2QGT8ZIZN8q2WPSFDC32+sZGKgp49gvnHzIf/vKVR4xIzjcgBbjAIKI+FqfGBmGBWu8eCpS7lR9JDXdUHOFkw=="; };
"@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=446fd4e36e9700cf27ce20b472a8d512133e9370" = { filename = "@quixos-camino-package-runtime-https-84d8fd5109-6d173f9220.zip"; hash = "sha512-bRc/kiD/ODsJqBNX4+RTqdsczILibHLhB+X8MckDCoK+j32c4Hszh87obb1Rwjb5o35aI0fzArQqCwK270zzlw=="; }; "@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=a998690446ecf647e3af78e369492c1a3663b66d" = { filename = "@quixos-camino-package-runtime-https-683bcbf33c-c90d5d5c53.zip"; hash = "sha512-yQ1dXFMR2k+xbkka2rrhsAcrQFZlJAZEYM0hqwGzdV5RJ+fhQEh3BEgoPiL2yxrxulU4zg/jsj8BTBFRp1feyg=="; };
"@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; }; "@types/node@npm:24.13.3" = { filename = "@types-node-npm-24.13.3-b512a0bbeb-a5bc08f49b.zip"; hash = "sha512-pbwI9JuVgdzcqQ4CzXcZejx5mECAdmSULlzVFhpVPU0q6AZPfjKUQQ10jX0Ji1wYSL5/pQwG8pxBk6sWvk5Z6w=="; };
"@types/prop-types@npm:15.7.15" = { filename = "@types-prop-types-npm-15.7.15-cefe16a1fa-b59aad1ad1.zip"; hash = "sha512-tZqtGtGb8XM89ST9TmGBlsbHaQ9I7nCjJ+tFCkKquOigY/vlnKClcBrr4tktWCKSwPuEXqV0dPahX2mUsOJgsg=="; };
"@types/react@npm:18.3.31" = { filename = "@types-react-npm-18.3.31-d376eb19a5-44180549dd.zip"; hash = "sha512-RBgFSd0EX1Nuzs0545qs34KOdq3BxKkLEy9FPiPMNwxGSNkQKuQBFy69j9ixl3qQGjniFOU+x3FxsnUUtYjBeQ=="; };
"@typescript/typescript-linux-arm64@npm:7.0.2" = { filename = "@typescript-typescript-linux-arm64-npm-7.0.2-9cacfc53a0-10c0.zip"; hash = "sha512-s4c7nGT/lQIw6x0IfrL0b3ECy/D0g7UoDLdiIWJoM6EGXk5N/dmXdxUdA8Cr3hV/3nO2BYKTQ6Ai8/zGhiSQ3A=="; }; "@typescript/typescript-linux-arm64@npm:7.0.2" = { filename = "@typescript-typescript-linux-arm64-npm-7.0.2-9cacfc53a0-10c0.zip"; hash = "sha512-s4c7nGT/lQIw6x0IfrL0b3ECy/D0g7UoDLdiIWJoM6EGXk5N/dmXdxUdA8Cr3hV/3nO2BYKTQ6Ai8/zGhiSQ3A=="; };
"@typescript/typescript-linux-x64@npm:7.0.2" = { filename = "@typescript-typescript-linux-x64-npm-7.0.2-8dd7ff8d24-10c0.zip"; hash = "sha512-k9EUK5CZ3OOPnyslp8SAAonHJpnpPmlaulppI/NmFyCzNs3UN/TLPUkyQia3KIxgbyTzKoXI5L5fl8Uj9bJ7Cg=="; }; "@typescript/typescript-linux-x64@npm:7.0.2" = { filename = "@typescript-typescript-linux-x64-npm-7.0.2-8dd7ff8d24-10c0.zip"; hash = "sha512-k9EUK5CZ3OOPnyslp8SAAonHJpnpPmlaulppI/NmFyCzNs3UN/TLPUkyQia3KIxgbyTzKoXI5L5fl8Uj9bJ7Cg=="; };
"csstype@npm:3.2.3" = { filename = "csstype-npm-3.2.3-741053244e-cd29c51e70.zip"; hash = "sha512-zSnFHnD6gi8c7NhkGhRFvtcGNpdGnTVjO1FuYP6MG94EsI9sW2AiE2u2abZMY9QXOvVIZFEPu07iMoGAGEGjzg=="; };
"esbuild@npm:0.25.12" = { filename = "esbuild-npm-0.25.12-352393c4bd-c205357531.zip"; hash = "sha512-wgU1dTFCMiCp3o4ebGUUJCvJsWZudizWfM34/f3D8dC9dvjZODlYuXrUyVPv23tujB+cpZUc0rfFI16HVbNKaw=="; }; "esbuild@npm:0.25.12" = { filename = "esbuild-npm-0.25.12-352393c4bd-c205357531.zip"; hash = "sha512-wgU1dTFCMiCp3o4ebGUUJCvJsWZudizWfM34/f3D8dC9dvjZODlYuXrUyVPv23tujB+cpZUc0rfFI16HVbNKaw=="; };
"js-tokens@npm:4.0.0" = { filename = "js-tokens-npm-4.0.0-0ac852e9e2-e248708d37.zip"; hash = "sha512-4khwjTd6oFjqzyA3sH3thHeQ5t6JK7rT2sCrui51nLnxIbAAmaZRlWFrrctuyo0U2XXLPonrHP2mRHVkAsiu7Q=="; };
"loose-envify@npm:1.4.0" = { filename = "loose-envify-npm-1.4.0-6307b72ccf-655d110220.zip"; hash = "sha512-ZV0RAiCYPBpLnAxnmi6AFtS2f26ce1Q1/1l57Nsg0IE/TewKCGdPy91IRqPwftu1CjaBH9N5MLlKqg2drOsBfg=="; };
"react@npm:18.3.1" = { filename = "react-npm-18.3.1-af38f3c1ae-283e8c5efc.zip"; hash = "sha512-KD6MXvzzeALJ0c52fzAt1Wndl6cNm7jHvnmnibmQJFHg0WM0sF1zKZsg8EjLw8fSiLu94QtwH6GU4gicI32+ow=="; };
"typescript@npm:7.0.2" = { filename = "typescript-npm-7.0.2-0bde2bcec5-3dcee51ec8.zip"; hash = "sha512-Pc7lHsjDMkkjJbzb999ItmZodR8SfmIq59QbbHFusZGEQkpF4U93UPUPNAIyOItp5ih4WRVfBqXOLfdvEssxzw=="; }; "typescript@npm:7.0.2" = { filename = "typescript-npm-7.0.2-0bde2bcec5-3dcee51ec8.zip"; hash = "sha512-Pc7lHsjDMkkjJbzb999ItmZodR8SfmIq59QbbHFusZGEQkpF4U93UPUPNAIyOItp5ih4WRVfBqXOLfdvEssxzw=="; };
"typescript@patch:typescript@npm%3A7.0.2#optional!builtin<compat/typescript>::version=7.0.2&hash=3bafbf" = { filename = "typescript-patch-4859cbaf66-6b3cdc369c.zip"; hash = "sha512-azzcNpzYKdRuAHNOpkIz7oRBnCglrY/kCJFa3ed6vO1LeiQB9+7VF9VKek9dHBhpdTy5kBjfHCFZx2HZ8zSDpg=="; }; "typescript@patch:typescript@npm%3A7.0.2#optional!builtin<compat/typescript>::version=7.0.2&hash=3bafbf" = { filename = "typescript-patch-4859cbaf66-6b3cdc369c.zip"; hash = "sha512-azzcNpzYKdRuAHNOpkIz7oRBnCglrY/kCJFa3ed6vO1LeiQB9+7VF9VKek9dHBhpdTy5kBjfHCFZx2HZ8zSDpg=="; };
"undici-types@npm:7.18.2" = { filename = "undici-types-npm-7.18.2-3e6d69d829-85a7918911.zip"; hash = "sha512-haeRiRE6I4lZ16ZHNo5PfFVZw6QE69uPxEiBRc6UJvzYIlKoRKMCeY38Djfm+xeP9IHtA7xMr2NMV1fZ70NSHQ=="; }; "undici-types@npm:7.18.2" = { filename = "undici-types-npm-7.18.2-3e6d69d829-85a7918911.zip"; hash = "sha512-haeRiRE6I4lZ16ZHNo5PfFVZw6QE69uPxEiBRc6UJvzYIlKoRKMCeY38Djfm+xeP9IHtA7xMr2NMV1fZ70NSHQ=="; };
+4 -57
View File
@@ -213,14 +213,14 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=446fd4e36e9700cf27ce20b472a8d512133e9370": "@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=a998690446ecf647e3af78e369492c1a3663b66d":
version: 0.1.0 version: 0.1.0
resolution: "@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=446fd4e36e9700cf27ce20b472a8d512133e9370" resolution: "@quixos/camino-package-runtime@https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=a998690446ecf647e3af78e369492c1a3663b66d"
dependencies: dependencies:
"@bufbuild/protobuf": "npm:^2.12.1" "@bufbuild/protobuf": "npm:^2.12.1"
"@connectrpc/connect": "npm:^2.1.2" "@connectrpc/connect": "npm:^2.1.2"
"@connectrpc/connect-node": "npm:^2.1.2" "@connectrpc/connect-node": "npm:^2.1.2"
checksum: 10c0/6d173f9220ff383b09a81357e3e453a9db1ccc82e26c72e107e5fc31c9030a82be8f7d9ce07b3387cee86dbd51c236f9a37e5a2347f302b42a0b02b6ef4cf397 checksum: 10c0/c90d5d5c5311da4fb16e491adabae1b0072b40566524064460cd21ab01b3755e5127e7e14048770448283e22f6cb1af1ba5538ce0fe3b23f014c1151a757deca
languageName: node languageName: node
linkType: hard linkType: hard
@@ -228,11 +228,9 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@quixos/todo-react-runtime@workspace:." resolution: "@quixos/todo-react-runtime@workspace:."
dependencies: dependencies:
"@quixos/camino-package-runtime": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=446fd4e36e9700cf27ce20b472a8d512133e9370" "@quixos/camino-package-runtime": "https://gitea-external.egads.tutti.syntaxblitz.net/quixos/camino-package-runtime.git#commit=a998690446ecf647e3af78e369492c1a3663b66d"
"@types/node": "npm:^24" "@types/node": "npm:^24"
"@types/react": "npm:^18.3.12"
esbuild: "npm:^0.25.12" esbuild: "npm:^0.25.12"
react: "npm:^18.3.1"
typescript: "npm:^7.0.2" typescript: "npm:^7.0.2"
bin: bin:
server: dist/server.js server: dist/server.js
@@ -248,23 +246,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/prop-types@npm:*":
version: 15.7.15
resolution: "@types/prop-types@npm:15.7.15"
checksum: 10c0/b59aad1ad19bf1733cf524fd4e618196c6c7690f48ee70a327eb450a42aab8e8a063fbe59ca0a5701aebe2d92d582292c0fb845ea57474f6a15f6994b0e260b2
languageName: node
linkType: hard
"@types/react@npm:^18.3.12":
version: 18.3.31
resolution: "@types/react@npm:18.3.31"
dependencies:
"@types/prop-types": "npm:*"
csstype: "npm:^3.2.2"
checksum: 10c0/44180549dd045f536ececd39e39aacdf828e76adc1c4a90b132f453e23cc370c4648d9102ae401172ebd8fd8b1977a901a39e214e53ec77171b27514b588c179
languageName: node
linkType: hard
"@typescript/typescript-aix-ppc64@npm:7.0.2": "@typescript/typescript-aix-ppc64@npm:7.0.2":
version: 7.0.2 version: 7.0.2
resolution: "@typescript/typescript-aix-ppc64@npm:7.0.2" resolution: "@typescript/typescript-aix-ppc64@npm:7.0.2"
@@ -405,13 +386,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"csstype@npm:^3.2.2":
version: 3.2.3
resolution: "csstype@npm:3.2.3"
checksum: 10c0/cd29c51e70fa822f1cecd8641a1445bed7063697469d35633b516e60fe8c1bde04b08f6c5b6022136bb669b64c63d4173af54864510fbb4ee23281801841a3ce
languageName: node
linkType: hard
"esbuild@npm:^0.25.12": "esbuild@npm:^0.25.12":
version: 0.25.12 version: 0.25.12
resolution: "esbuild@npm:0.25.12" resolution: "esbuild@npm:0.25.12"
@@ -501,33 +475,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"js-tokens@npm:^3.0.0 || ^4.0.0":
version: 4.0.0
resolution: "js-tokens@npm:4.0.0"
checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed
languageName: node
linkType: hard
"loose-envify@npm:^1.1.0":
version: 1.4.0
resolution: "loose-envify@npm:1.4.0"
dependencies:
js-tokens: "npm:^3.0.0 || ^4.0.0"
bin:
loose-envify: cli.js
checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e
languageName: node
linkType: hard
"react@npm:^18.3.1":
version: 18.3.1
resolution: "react@npm:18.3.1"
dependencies:
loose-envify: "npm:^1.1.0"
checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3
languageName: node
linkType: hard
"typescript@npm:^7.0.2": "typescript@npm:^7.0.2":
version: 7.0.2 version: 7.0.2
resolution: "typescript@npm:7.0.2" resolution: "typescript@npm:7.0.2"