@miadi/stateloom-web 0.1.7 → 0.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/package.json +1 -1
- package/server/.next/BUILD_ID +1 -1
- package/server/.next/build-manifest.json +2 -2
- package/server/.next/server/app/_global-error.html +2 -2
- package/server/.next/server/app/_global-error.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/server/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/server/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/server/.next/server/app/_not-found.html +1 -1
- package/server/.next/server/app/_not-found.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/server/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/server/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/server/.next/server/app/api/docs/route.js.nft.json +1 -1
- package/server/.next/server/app/api/file/route.js.nft.json +1 -1
- package/server/.next/server/app/api/rispec/route.js.nft.json +1 -1
- package/server/.next/server/app/api/watch/route.js.nft.json +1 -1
- package/server/.next/server/app/index.html +1 -1
- package/server/.next/server/app/index.rsc +3 -3
- package/server/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/server/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/server/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/server/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/server/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/server/.next/server/app/page.js.nft.json +1 -1
- package/server/.next/server/app/page_client-reference-manifest.js +1 -1
- package/server/.next/server/chunks/ssr/{[root-of-the-server]__46d45ef8._.js → [root-of-the-server]__d5879c0c._.js} +1 -1
- package/server/.next/server/chunks/ssr/[root-of-the-server]__edbb995c._.js +3 -3
- package/server/.next/server/pages/404.html +1 -1
- package/server/.next/server/pages/500.html +2 -2
- package/server/.next/static/chunks/636e7eb4aeda72e0.css +2 -0
- package/server/.next/static/chunks/92628504da59dcc8.js +10 -0
- package/server/package-lock.json +2 -2
- package/server/package.json +1 -1
- package/server/src/app/page.tsx +17 -10
- package/server/src/components/IssueIcon.tsx +28 -0
- package/server/src/components/LockIcon.tsx +24 -0
- package/server/src/components/PropertiesPanel.tsx +10 -0
- package/server/src/components/SettingsPanel.tsx +11 -0
- package/server/src/components/erd/ErdWorkspace.tsx +564 -172
- package/server/src/lib/useSheetDrag.ts +95 -0
- package/server/src/types/definition.ts +4 -0
- package/server/stateloom-web.json +2 -2
- package/server/tsconfig.tsbuildinfo +1 -1
- package/server/.next/static/chunks/486bc3bc16e91024.css +0 -2
- package/server/.next/static/chunks/b3f3eb72c25c4c6b.js +0 -10
- /package/server/.next/static/{DPL_H0tteH9tpIBvvsIfq → 6dAVJYqnbe6oEAABQ1yMG}/_buildManifest.js +0 -0
- /package/server/.next/static/{DPL_H0tteH9tpIBvvsIfq → 6dAVJYqnbe6oEAABQ1yMG}/_clientMiddlewareManifest.json +0 -0
- /package/server/.next/static/{DPL_H0tteH9tpIBvvsIfq → 6dAVJYqnbe6oEAABQ1yMG}/_ssgManifest.js +0 -0
package/server/src/app/page.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { useEffect, useState, type CSSProperties } from "react";
|
|
3
|
+
import { useEffect, useState, type CSSProperties, type ReactNode } from "react";
|
|
4
4
|
import Canvas from "@/components/Canvas";
|
|
5
5
|
import PropertiesPanel from "@/components/PropertiesPanel";
|
|
6
6
|
import EventsPanel from "@/components/EventsPanel";
|
|
@@ -12,9 +12,11 @@ import DesignBridge from "@/components/DesignBridge";
|
|
|
12
12
|
import UiScale from "@/components/UiScale";
|
|
13
13
|
import { useDesignerStore } from "@/store/useDesignerStore";
|
|
14
14
|
import ErdWorkspace from "@/components/erd/ErdWorkspace";
|
|
15
|
+
import IssueIcon from "@/components/IssueIcon";
|
|
15
16
|
import { isErdfPath } from "@miadi/stateloom-protocol";
|
|
16
17
|
import { useRequestedDoc } from "@/lib/docParam";
|
|
17
18
|
import { loadRuntimeConfig } from "@/lib/runtimeConfig";
|
|
19
|
+
import { useSheetDrag } from "@/lib/useSheetDrag";
|
|
18
20
|
|
|
19
21
|
type Tab = "properties" | "events" | "settings" | "validation";
|
|
20
22
|
|
|
@@ -78,6 +80,8 @@ function MachineDesigner() {
|
|
|
78
80
|
// the entire screen, and at `md` and up the responsive classes pin it open as
|
|
79
81
|
// the 320px right sidebar it has always been — this flag stops mattering.
|
|
80
82
|
const [sheetOpen, setSheetOpen] = useState(false);
|
|
83
|
+
// The sheet's handle answers a thumb: pull down to close, up to grow, tap to toggle.
|
|
84
|
+
const sheet = useSheetDrag({ onClose: () => setSheetOpen(false), restVh: 70, tallVh: 92 });
|
|
81
85
|
const errors = useDesignerStore((s) => s.errors);
|
|
82
86
|
const contextMenu = useDesignerStore((s) => s.contextMenu);
|
|
83
87
|
const hideContextMenu = useDesignerStore((s) => s.hideContextMenu);
|
|
@@ -86,11 +90,12 @@ function MachineDesigner() {
|
|
|
86
90
|
const setDrawMode = useDesignerStore((s) => s.setDrawMode);
|
|
87
91
|
const setDrawSource = useDesignerStore((s) => s.setDrawSource);
|
|
88
92
|
|
|
89
|
-
const tabs: { id: Tab; label: string; icon:
|
|
93
|
+
const tabs: { id: Tab; label: string; icon: ReactNode; badge?: number }[] = [
|
|
90
94
|
{ id: "properties", label: "Properties", icon: "🎛" },
|
|
91
95
|
{ id: "events", label: "Events", icon: "⚡" },
|
|
92
96
|
{ id: "settings", label: "Settings", icon: "⚙" },
|
|
93
|
-
|
|
97
|
+
// Drawn, not typed: "⚠" is a bright yellow colour emoji on iOS. See IssueIcon.
|
|
98
|
+
{ id: "validation", label: "Errors", icon: <IssueIcon />, badge: errors.length || undefined },
|
|
94
99
|
];
|
|
95
100
|
|
|
96
101
|
const activeLabel = tabs.find((t) => t.id === activeTab)?.label ?? "";
|
|
@@ -99,7 +104,8 @@ function MachineDesigner() {
|
|
|
99
104
|
// both switcher and dismiss control and no screen height is spent on a
|
|
100
105
|
// separate close affordance.
|
|
101
106
|
const toggleTab = (id: Tab) => {
|
|
102
|
-
|
|
107
|
+
if (sheetOpen && activeTab === id) sheet.close();
|
|
108
|
+
else setSheetOpen(true);
|
|
103
109
|
setActiveTab(id);
|
|
104
110
|
};
|
|
105
111
|
|
|
@@ -125,7 +131,7 @@ function MachineDesigner() {
|
|
|
125
131
|
{sheetOpen && (
|
|
126
132
|
<div
|
|
127
133
|
className="fixed inset-0 z-20 bg-black/50 md:hidden"
|
|
128
|
-
onClick={
|
|
134
|
+
onClick={sheet.close}
|
|
129
135
|
aria-hidden="true"
|
|
130
136
|
/>
|
|
131
137
|
)}
|
|
@@ -136,21 +142,22 @@ function MachineDesigner() {
|
|
|
136
142
|
declaration — position, height, width, transform, radius, border
|
|
137
143
|
side — which is why the class list is long. */}
|
|
138
144
|
<aside
|
|
139
|
-
|
|
145
|
+
style={sheet.sheetStyle}
|
|
146
|
+
className={`safe-x fixed inset-x-0 bottom-0 z-30 h-[var(--sheet-h)] flex flex-col overflow-hidden rounded-t-2xl border-t border-gray-800 bg-gray-900 shadow-2xl transition-[transform,height] duration-200 ease-out ${
|
|
140
147
|
sheetOpen ? "translate-y-0" : "translate-y-full pointer-events-none"
|
|
141
148
|
} md:static md:z-auto md:h-auto md:w-80 md:translate-y-0 md:pointer-events-auto md:rounded-none md:border-t-0 md:border-l md:shadow-none md:transition-none`}
|
|
142
149
|
>
|
|
143
150
|
{/* Sheet header (phone). The grab handle reads as "this thing is a
|
|
144
151
|
sheet"; the dock below already names the tabs, so repeating the
|
|
145
152
|
tab bar here would only cost height. */}
|
|
146
|
-
<div className="md:hidden">
|
|
153
|
+
<div className="md:hidden" {...sheet.zoneProps}>
|
|
147
154
|
<div className="flex justify-center pt-2 pb-1">
|
|
148
|
-
<span className="h-1 w-
|
|
155
|
+
<span className="h-1.5 w-12 rounded-full bg-gray-600" />
|
|
149
156
|
</div>
|
|
150
157
|
<div className="flex items-center justify-between border-b border-gray-800 px-4 pb-2">
|
|
151
158
|
<h2 className="text-sm font-semibold text-gray-300">{activeLabel}</h2>
|
|
152
159
|
<button
|
|
153
|
-
onClick={
|
|
160
|
+
onClick={sheet.close}
|
|
154
161
|
className="-mr-2 px-3 py-2 text-lg text-gray-400"
|
|
155
162
|
aria-label="Close panel"
|
|
156
163
|
>
|
|
@@ -215,7 +222,7 @@ function MachineDesigner() {
|
|
|
215
222
|
current ? "text-blue-400" : "text-gray-500"
|
|
216
223
|
}`}
|
|
217
224
|
>
|
|
218
|
-
<span className="text-base leading-none">{tab.icon}</span>
|
|
225
|
+
<span className="flex h-4 items-center text-base leading-none">{tab.icon}</span>
|
|
219
226
|
<span>{tab.label}</span>
|
|
220
227
|
{tab.badge !== undefined && tab.badge > 0 && (
|
|
221
228
|
<span className="absolute top-1 right-1/2 translate-x-4 rounded-full bg-red-600 px-1 text-[10px] leading-4 text-white min-w-[16px]">
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A warning triangle drawn as an outline in the current text colour.
|
|
3
|
+
*
|
|
4
|
+
* The "⚠" character is a full-colour emoji on iOS: a saturated yellow sign in
|
|
5
|
+
* a dock of grey glyphs, pulling the eye whether or not there is anything to
|
|
6
|
+
* report. Drawn here instead, it is as quiet as its neighbours and takes the
|
|
7
|
+
* tab's own colour — grey at rest, the accent when its tab is open. The count
|
|
8
|
+
* badge beside it is what says there is something to look at.
|
|
9
|
+
*/
|
|
10
|
+
export default function IssueIcon({ size = 16 }: { size?: number }) {
|
|
11
|
+
return (
|
|
12
|
+
<svg
|
|
13
|
+
width={size}
|
|
14
|
+
height={size}
|
|
15
|
+
viewBox="0 0 24 24"
|
|
16
|
+
fill="none"
|
|
17
|
+
stroke="currentColor"
|
|
18
|
+
strokeWidth="1.6"
|
|
19
|
+
strokeLinecap="round"
|
|
20
|
+
strokeLinejoin="round"
|
|
21
|
+
aria-hidden="true"
|
|
22
|
+
>
|
|
23
|
+
<path d="M12 4.5 21 19.5H3Z" />
|
|
24
|
+
<path d="M12 10v4.5" />
|
|
25
|
+
<path d="M12 17.3v.01" />
|
|
26
|
+
</svg>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A padlock drawn as an outline in the current text colour — closed or open.
|
|
3
|
+
* Drawn rather than typed for the same reason as IssueIcon: the padlock
|
|
4
|
+
* characters are full-colour emoji on iOS, a gold lock on a board of grey.
|
|
5
|
+
*/
|
|
6
|
+
export default function LockIcon({ locked, size = 16 }: { locked: boolean; size?: number }) {
|
|
7
|
+
return (
|
|
8
|
+
<svg
|
|
9
|
+
width={size}
|
|
10
|
+
height={size}
|
|
11
|
+
viewBox="0 0 24 24"
|
|
12
|
+
fill="none"
|
|
13
|
+
stroke="currentColor"
|
|
14
|
+
strokeWidth="1.6"
|
|
15
|
+
strokeLinecap="round"
|
|
16
|
+
strokeLinejoin="round"
|
|
17
|
+
aria-hidden="true"
|
|
18
|
+
>
|
|
19
|
+
<rect x="5" y="11" width="14" height="9" rx="2" />
|
|
20
|
+
{/* The shackle: seated in the body when locked, swung clear of it when not. */}
|
|
21
|
+
<path d={locked ? "M8 11V8a4 4 0 0 1 8 0v3" : "M8 11V8a4 4 0 0 1 7.5-1.9"} />
|
|
22
|
+
</svg>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
@@ -116,6 +116,16 @@ export default function PropertiesPanel() {
|
|
|
116
116
|
onChange={(e) => updateState(selection.id!, { description: e.target.value || undefined })}
|
|
117
117
|
/>
|
|
118
118
|
</label>
|
|
119
|
+
{/* Notes: what was said about this state, kept in the document for whoever opens it next. */}
|
|
120
|
+
<label className="block">
|
|
121
|
+
<span className="text-xs text-gray-500">Notes</span>
|
|
122
|
+
<textarea
|
|
123
|
+
className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1 text-xs text-gray-200 h-20"
|
|
124
|
+
placeholder="An open question, a decision, something to tell the agent…"
|
|
125
|
+
value={state.notes ?? ""}
|
|
126
|
+
onChange={(e) => updateState(selection.id!, { notes: e.target.value || undefined })}
|
|
127
|
+
/>
|
|
128
|
+
</label>
|
|
119
129
|
|
|
120
130
|
{/* onEntry actions */}
|
|
121
131
|
<ActionEditor
|
|
@@ -38,6 +38,17 @@ export default function SettingsPanel() {
|
|
|
38
38
|
/>
|
|
39
39
|
</label>
|
|
40
40
|
|
|
41
|
+
{/* Notes about the whole diagram, kept in the document for whoever opens it next. */}
|
|
42
|
+
<label className="block">
|
|
43
|
+
<span className="text-xs text-gray-500">Notes on this diagram</span>
|
|
44
|
+
<textarea
|
|
45
|
+
className="w-full bg-gray-800 border border-gray-700 rounded px-2 py-1 text-xs text-gray-200 h-24"
|
|
46
|
+
placeholder="What was discussed, what is still open, what to tell the agent…"
|
|
47
|
+
value={settings.notes ?? ""}
|
|
48
|
+
onChange={(e) => updateSettings({ notes: e.target.value || undefined })}
|
|
49
|
+
/>
|
|
50
|
+
</label>
|
|
51
|
+
|
|
41
52
|
<label className="flex items-center gap-2 text-sm text-gray-300">
|
|
42
53
|
<input
|
|
43
54
|
type="checkbox"
|