@almadar/std 16.241.0 → 16.243.0
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/behaviors/behaviors-registry.json +4650 -535
- package/behaviors/lolo/agent/atoms/std-agent-memory.lolo +38 -1
- package/behaviors/lolo/agent/atoms/std-agent-session.lolo +1074 -47
- package/behaviors/lolo/agent/atoms/std-agent-tool-loop.lolo +8 -7
- package/behaviors/lolo/agent/organisms/std-agent-assistant.lolo +56 -37
- package/behaviors/lolo/infra/atoms/std-page-open.lolo +59 -0
- package/behaviors/lolo/infra/atoms/std-page-search.lolo +136 -0
- package/behaviors/lolo/infra/atoms/std-page-treat.lolo +59 -0
- package/behaviors/lolo/infra/atoms/std-page-watch.lolo +88 -0
- package/behaviors/lolo/ui/core/atoms/std-book-reader.lolo +25 -1
- package/behaviors/lolo/ui/core/atoms/std-card-grid.lolo +9 -0
- package/behaviors/lolo/ui/core/atoms/std-doc-page.lolo +153 -0
- package/behaviors/lolo/ui/learning/atoms/std-learn-arrangement.lolo +124 -92
- package/behaviors/lolo/ui/learning/atoms/std-learn-classify.lolo +114 -55
- package/behaviors/lolo/ui/learning/atoms/std-learn-compare.lolo +40 -14
- package/behaviors/lolo/ui/learning/atoms/std-learn-coupled-sim.lolo +363 -123
- package/behaviors/lolo/ui/learning/atoms/std-learn-derivation.lolo +93 -48
- package/behaviors/lolo/ui/learning/atoms/std-learn-diagram.lolo +60 -28
- package/behaviors/lolo/ui/learning/atoms/std-learn-doser.lolo +193 -96
- package/behaviors/lolo/ui/learning/atoms/std-learn-estimate.lolo +75 -32
- package/behaviors/lolo/ui/learning/atoms/std-learn-goal-seek.lolo +142 -86
- package/behaviors/lolo/ui/learning/atoms/std-learn-grid-sim.lolo +266 -98
- package/behaviors/lolo/ui/learning/atoms/std-learn-hotspot.lolo +103 -65
- package/behaviors/lolo/ui/learning/atoms/std-learn-live-sim.lolo +223 -109
- package/behaviors/lolo/ui/learning/atoms/std-learn-measure.lolo +232 -64
- package/behaviors/lolo/ui/learning/atoms/std-learn-param-explorer.lolo +69 -29
- package/behaviors/lolo/ui/learning/atoms/std-learn-perturb.lolo +363 -123
- package/behaviors/lolo/ui/learning/atoms/std-learn-predict.lolo +73 -41
- package/behaviors/lolo/ui/learning/atoms/std-learn-sample-accumulator.lolo +96 -38
- package/behaviors/lolo/ui/learning/atoms/std-learn-sandbox.lolo +65 -45
- package/behaviors/lolo/ui/learning/atoms/std-learn-scatter-fit.lolo +243 -117
- package/behaviors/lolo/ui/learning/atoms/std-learn-scenario.lolo +76 -28
- package/behaviors/lolo/ui/learning/atoms/std-learn-sim-3d.lolo +228 -109
- package/behaviors/lolo/ui/learning/atoms/std-learn-speed-drill.lolo +95 -44
- package/behaviors/lolo/ui/learning/atoms/std-learn-stepper.lolo +199 -125
- package/behaviors/lolo/ui/learning/atoms/std-learn-tour-3d.lolo +92 -48
- package/behaviors/lolo/ui/learning/atoms/std-learn-trace-player.lolo +239 -78
- package/behaviors/lolo/ui/learning/atoms/std-learn-transport.lolo +147 -38
- package/behaviors/lolo/ui/learning/atoms/std-learn-tree.lolo +123 -58
- package/behaviors/lolo/ui/learning/atoms/ui-algo-graph-canvas.lolo +22 -0
- package/behaviors/lolo/ui/learning/atoms/ui-algorithm-canvas.lolo +26 -0
- package/behaviors/lolo/ui/learning/atoms/ui-biology-canvas.lolo +26 -0
- package/behaviors/lolo/ui/learning/atoms/ui-chemistry-canvas.lolo +27 -1
- package/behaviors/lolo/ui/learning/atoms/ui-learning-canvas.lolo +27 -1
- package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +29 -9
- package/behaviors/lolo/ui/learning/atoms/ui-physics-canvas.lolo +26 -0
- package/behaviors/lolo-behavior-contracts.json +1 -1
- package/behaviors/registry/agent/atoms/std-agent-memory.orb +372 -2
- package/behaviors/registry/agent/atoms/std-agent-session.orb +4752 -348
- package/behaviors/registry/agent/atoms/std-agent-tool-loop.orb +54 -42
- package/behaviors/registry/agent/organisms/std-agent-assistant.orb +356 -122
- package/behaviors/registry/factory-signatures.json +1 -1
- package/behaviors/registry/infra/atoms/std-page-open.orb +435 -0
- package/behaviors/registry/infra/atoms/std-page-search.orb +885 -0
- package/behaviors/registry/infra/atoms/std-page-treat.orb +494 -0
- package/behaviors/registry/infra/atoms/std-page-watch.orb +572 -0
- package/behaviors/registry/ui/core/atoms/std-book-reader.orb +119 -0
- package/behaviors/registry/ui/core/atoms/std-card-grid.orb +61 -0
- package/behaviors/registry/ui/core/atoms/std-doc-page.orb +594 -0
- package/behaviors/registry/ui/learning/atoms/std-learn-arrangement.orb +903 -369
- package/behaviors/registry/ui/learning/atoms/std-learn-classify.orb +1002 -232
- package/behaviors/registry/ui/learning/atoms/std-learn-compare.orb +253 -74
- package/behaviors/registry/ui/learning/atoms/std-learn-coupled-sim.orb +3624 -921
- package/behaviors/registry/ui/learning/atoms/std-learn-derivation.orb +685 -144
- package/behaviors/registry/ui/learning/atoms/std-learn-diagram.orb +404 -52
- package/behaviors/registry/ui/learning/atoms/std-learn-doser.orb +1787 -210
- package/behaviors/registry/ui/learning/atoms/std-learn-estimate.orb +699 -71
- package/behaviors/registry/ui/learning/atoms/std-learn-goal-seek.orb +1102 -578
- package/behaviors/registry/ui/learning/atoms/std-learn-grid-sim.orb +2760 -872
- package/behaviors/registry/ui/learning/atoms/std-learn-hotspot.orb +689 -152
- package/behaviors/registry/ui/learning/atoms/std-learn-live-sim.orb +1431 -633
- package/behaviors/registry/ui/learning/atoms/std-learn-measure.orb +2485 -512
- package/behaviors/registry/ui/learning/atoms/std-learn-param-explorer.orb +501 -197
- package/behaviors/registry/ui/learning/atoms/std-learn-perturb.orb +3642 -928
- package/behaviors/registry/ui/learning/atoms/std-learn-predict.orb +705 -125
- package/behaviors/registry/ui/learning/atoms/std-learn-sample-accumulator.orb +624 -114
- package/behaviors/registry/ui/learning/atoms/std-learn-sandbox.orb +405 -153
- package/behaviors/registry/ui/learning/atoms/std-learn-scatter-fit.orb +1911 -611
- package/behaviors/registry/ui/learning/atoms/std-learn-scenario.orb +436 -128
- package/behaviors/registry/ui/learning/atoms/std-learn-sim-3d.orb +2482 -485
- package/behaviors/registry/ui/learning/atoms/std-learn-speed-drill.orb +896 -192
- package/behaviors/registry/ui/learning/atoms/std-learn-stepper.orb +2001 -759
- package/behaviors/registry/ui/learning/atoms/std-learn-tour-3d.orb +734 -228
- package/behaviors/registry/ui/learning/atoms/std-learn-trace-player.orb +2501 -764
- package/behaviors/registry/ui/learning/atoms/std-learn-transport.orb +1591 -173
- package/behaviors/registry/ui/learning/atoms/std-learn-tree.orb +883 -122
- package/behaviors/registry/ui/learning/atoms/ui-algo-graph-canvas.orb +176 -0
- package/behaviors/registry/ui/learning/atoms/ui-algorithm-canvas.orb +211 -0
- package/behaviors/registry/ui/learning/atoms/ui-biology-canvas.orb +211 -0
- package/behaviors/registry/ui/learning/atoms/ui-chemistry-canvas.orb +212 -1
- package/behaviors/registry/ui/learning/atoms/ui-learning-canvas.orb +212 -1
- package/behaviors/registry/ui/learning/atoms/ui-math-canvas.orb +203 -9
- package/behaviors/registry/ui/learning/atoms/ui-physics-canvas.orb +211 -0
- package/behaviors/signatures.lolo +325 -655
- package/dist/behaviors/exports-reader.js +16092 -9747
- package/dist/behaviors/functions/index.d.ts +1052 -136
- package/dist/behaviors/functions/index.js +16063 -9745
- package/dist/behaviors/index.d.ts +1 -1
- package/dist/behaviors/index.js +16093 -9748
- package/dist/behaviors/query.js +16092 -9747
- package/dist/behaviors-embeddings.hash.json +3 -3
- package/dist/behaviors-embeddings.json +45 -25
- package/dist/behaviors-registry.json +4650 -535
- package/dist/index.d.ts +1 -1
- package/dist/index.js +16093 -9748
- package/dist/knob-embeddings.hash.json +3 -3
- package/dist/knob-embeddings.json +607 -263
- package/dist/registry/factory-signatures.json +1 -1
- package/package.json +3 -3
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
app std-agent-tool-loop "2.0.0"
|
|
2
|
-
"std-agent-tool-loop — answers a conversation by operating the running app as the caller, through `call-service llm call-tools`.
|
|
2
|
+
"std-agent-tool-loop — answers a conversation by operating the running app as the caller, through `call-service llm call-tools`. Its `tools` are `{ scope: declared }` (every declared external input plus a read over every persisted entity), or named declared inputs (addressed like a listens source: `Trait.EVENT`, `Orbital.Trait.EVENT`) and entities to read; each input fires as the signed-in user through the ordinary dispatch (guards and entity policies apply) and each read returns only the rows the caller may read. Headless: the conversation UI (std-agent-session) shows the reply; a failure shows as a toast. CONVERSE carries the conversation (and optionally what std-agent-memory recalled); ASSISTANT_STEP carries each step live while it works (a model call, a tool it used, what the app answered) to the asking tab; ASSISTANT_REPLIED carries the reply, every step and the token usage; ASSISTANT_REPLY_FAILED carries the error. These names are the assistant contract std-agent-session's conversation hears from any orbital. Compose with std-agent-session (the conversation) and std-agent-trace (the step record)."
|
|
3
3
|
|
|
4
4
|
type AgentTool = {
|
|
5
5
|
event : event
|
|
6
6
|
read : string
|
|
7
|
+
scope : "declared"
|
|
7
8
|
}
|
|
8
9
|
type AgentChatMessage = {
|
|
9
10
|
role : "user" | "assistant"
|
|
@@ -94,7 +95,7 @@ orbital AgentToolLoopOrbital {
|
|
|
94
95
|
instructions: (if ["=", (str/default ?context ""), ""] @config.instructions (str/concat @config.instructions "\n\nWhat you remember that may be relevant:\n" ?context))
|
|
95
96
|
maxSteps: @config.maxSteps
|
|
96
97
|
} {
|
|
97
|
-
emit: { failure: ASK_FAILED, success: ANSWERED, onMessage:
|
|
98
|
+
emit: { failure: ASK_FAILED, success: ANSWERED, onMessage: ASSISTANT_STEP }
|
|
98
99
|
})
|
|
99
100
|
}
|
|
100
101
|
|
|
@@ -102,11 +103,11 @@ orbital AgentToolLoopOrbital {
|
|
|
102
103
|
ANSWERED -> idle
|
|
103
104
|
(set @entity.status answered)
|
|
104
105
|
(set @entity.reply ?reply)
|
|
105
|
-
(emit
|
|
106
|
+
(emit ASSISTANT_REPLIED { reply: ?reply, steps: ?steps, usage: ?usage })
|
|
106
107
|
ASK_FAILED -> idle
|
|
107
108
|
(set @entity.status failed)
|
|
108
109
|
(set @entity.error ?error)
|
|
109
|
-
(emit
|
|
110
|
+
(emit ASSISTANT_REPLY_FAILED { error: ?error })
|
|
110
111
|
(render-ui toast {
|
|
111
112
|
message: ?error
|
|
112
113
|
type: alert
|
|
@@ -115,13 +116,13 @@ orbital AgentToolLoopOrbital {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
emits {
|
|
118
|
-
|
|
119
|
+
ASSISTANT_STEP -> external {
|
|
119
120
|
activity : AgentActivity!
|
|
120
121
|
}
|
|
121
122
|
@description "One thing the assistant just did, live while it works: a model call, a tool it used, or what the app answered. Delivered to the asking tab only."
|
|
122
123
|
@synonyms "progress, activity, trace step, working"
|
|
123
124
|
@tier "domain"
|
|
124
|
-
|
|
125
|
+
ASSISTANT_REPLIED -> external {
|
|
125
126
|
reply : string!
|
|
126
127
|
steps : [AgentActivity]!
|
|
127
128
|
usage : AgentUsage!
|
|
@@ -129,7 +130,7 @@ orbital AgentToolLoopOrbital {
|
|
|
129
130
|
@description "The assistant answered; carries every tool step it took and the tokens it used."
|
|
130
131
|
@synonyms "answered, responded, done"
|
|
131
132
|
@tier "domain"
|
|
132
|
-
|
|
133
|
+
ASSISTANT_REPLY_FAILED -> external {
|
|
133
134
|
error : string!
|
|
134
135
|
}
|
|
135
136
|
@description "The assistant could not answer (model unavailable, or out of steps)."
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
app std-agent-assistant "2.0.0"
|
|
2
|
-
"std-agent-assistant — an in-app assistant that operates the running app for the signed-in user.
|
|
2
|
+
"std-agent-assistant — an in-app assistant that operates the running app for the signed-in user. Each section's floating button opens it in a side panel docked beside the page (std-panel-launcher; a side sheet on small screens), where the conversation shows every step the assistant takes live as it works; /assistant is the same conversation as a full page, in the app's own shell (pass `appName` and `navItems`); past conversations and what the assistant remembers open in place from its toolbar (Back returns to the chat), on either surface. One request flows down a chain of std agent atoms on the bus: the conversation (std-agent-session) SENDs → CONVERSE → std-agent-context bounds it → std-agent-memory adds what it recalls → std-agent-tool-loop answers it through `call-service llm call-tools`, firing only the inputs and reading only the entities its `tools` allow, as the signed-in user → ASSISTANT_REPLIED goes back to the conversation, to memory (remembered) and to std-agent-trace (the run record). By default the tools are the program's own declarations: every `-> external` input (its @description is the tool text) and a read over every persisted entity, so composing building blocks composes the assistant's abilities. Any section's page gets the assistant with two traits, `XAssistant = AgentSession.traits.AgentConversation` and a std-panel-launcher dock: the conversation speaks the ASSISTANT_* contract from any orbital, so the assistant needs no per-section wiring. Import the orbital once per app: `orbital Assistant = Agent.orbitals.AgentAssistantOrbital { config { instructions: \"…\" } }`. The conversation, memory and run records live in AgentRecordsOrbital; retarget them onto your own owner-scoped entities (`ownerId` a relation to your [identity] roster, with @read/@create) with `entities { AgentMessage: …, AgentMemory: …, AgentRun: …, AgentChat: … }`. The conversation is kept across pages and reloads (each person's most recently used conversation is the current one);"
|
|
3
3
|
|
|
4
4
|
type AgentTool = {
|
|
5
5
|
event : event
|
|
6
6
|
read : string
|
|
7
|
+
scope : "declared"
|
|
7
8
|
}
|
|
8
9
|
type AgentLink = {
|
|
9
10
|
label : string!
|
|
10
11
|
href : string!
|
|
11
12
|
}
|
|
13
|
+
type NavItem = { label : string!, href : string!, icon : string, badge : string, children : [NavItem], roles : [string] }
|
|
12
14
|
|
|
13
15
|
orbital AgentAssistantOrbital {
|
|
14
16
|
uses Session from "std/behaviors/std-agent-session"
|
|
@@ -16,12 +18,12 @@ orbital AgentAssistantOrbital {
|
|
|
16
18
|
uses Memory from "std/behaviors/std-agent-memory"
|
|
17
19
|
uses Loop from "std/behaviors/std-agent-tool-loop"
|
|
18
20
|
uses Trace from "std/behaviors/std-agent-trace"
|
|
19
|
-
uses
|
|
21
|
+
uses Shell from "std/behaviors/std-app-layout"
|
|
20
22
|
|
|
21
23
|
config {
|
|
22
|
-
tools : [AgentTool] = []
|
|
24
|
+
tools : [AgentTool] = [{ scope: declared }]
|
|
23
25
|
@label "What may the assistant do?"
|
|
24
|
-
@description "
|
|
26
|
+
@description "As the signed-in user: `{ scope: declared }` (the default) gives it every external input the program declares and a read over every persisted entity, so each building block's declared inputs become its abilities; or list them — `{ event: Orbital.Trait.EVENT }` for one input, `{ read: Entity }` for one entity."
|
|
25
27
|
@synonyms "allowlist, abilities, actions, permissions"
|
|
26
28
|
@tier "domain"
|
|
27
29
|
instructions : string = "You operate this app for the signed-in user. Use the tools to act and to look things up; when a tool reports that something was refused or denied, say so plainly."
|
|
@@ -38,17 +40,22 @@ orbital AgentAssistantOrbital {
|
|
|
38
40
|
recallLimit : number = 3
|
|
39
41
|
@label "Memories recalled per request"
|
|
40
42
|
@tier "policy"
|
|
41
|
-
links : [AgentLink] = [{ label: "Run history", href: "/assistant/runs" }
|
|
43
|
+
links : [AgentLink] = [{ label: "Run history", href: "/assistant/runs" }]
|
|
42
44
|
@label "Links above the conversation"
|
|
43
|
-
@description "
|
|
45
|
+
@description "Related pages, e.g. the assistant's run history."
|
|
44
46
|
@tier "presentation"
|
|
45
|
-
|
|
46
|
-
@label "
|
|
47
|
+
appName : string = "Assistant"
|
|
48
|
+
@label "App name in the assistant page's header"
|
|
49
|
+
@description "Pass the app's own name so the assistant page matches the rest of the app."
|
|
47
50
|
@tier "presentation"
|
|
48
|
-
|
|
49
|
-
@label "
|
|
50
|
-
@description "
|
|
51
|
+
navItems : [NavItem] = []
|
|
52
|
+
@label "Sidebar navigation on the assistant page"
|
|
53
|
+
@description "Pass the app's own navItems so the assistant page keeps the app's sidebar."
|
|
51
54
|
@tier "presentation"
|
|
55
|
+
viewerRole : string = ""
|
|
56
|
+
@label "Viewer role on the assistant page"
|
|
57
|
+
@description "The role the assistant page's sidebar items and their `roles` are checked against. Bind `@user.role` in an app whose identity carries a role; left empty, only items with no `roles` show."
|
|
58
|
+
@tier "policy"
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
entity AgentAssistant [runtime] {
|
|
@@ -60,8 +67,20 @@ orbital AgentAssistantOrbital {
|
|
|
60
67
|
CONVERSE: REQUEST_CONTEXT
|
|
61
68
|
}
|
|
62
69
|
listens {
|
|
63
|
-
|
|
64
|
-
|
|
70
|
+
AssistantChatKeeper.ASSISTANT_CHAT_OPENED -> OPEN_CHAT
|
|
71
|
+
AssistantChatKeeper.ASSISTANT_SEND_IN_CHAT -> SEND_IN_CHAT
|
|
72
|
+
AssistantChatKeeper.ASSISTANT_REPLY_IN_CHAT -> REPLY_IN_CHAT
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
trait AssistantChatKeeper = Session.traits.AgentChatKeeper -> AgentChat {
|
|
77
|
+
listens {
|
|
78
|
+
*.ASSISTANT_SEND -> SEND
|
|
79
|
+
*.ASSISTANT_HISTORY_REQUESTED -> LOAD_HISTORY
|
|
80
|
+
*.ASSISTANT_NEW_CHAT -> NEW_CHAT
|
|
81
|
+
*.ASSISTANT_CHAT_SELECTED -> SELECT_CHAT
|
|
82
|
+
*.ASSISTANT_CHATS_REQUESTED -> LIST_CHATS
|
|
83
|
+
AssistantLoop.ASSISTANT_REPLIED -> REPLIED
|
|
65
84
|
}
|
|
66
85
|
}
|
|
67
86
|
|
|
@@ -69,21 +88,19 @@ orbital AgentAssistantOrbital {
|
|
|
69
88
|
config {
|
|
70
89
|
links: @config.links
|
|
71
90
|
}
|
|
72
|
-
listens {
|
|
73
|
-
AssistantThread.AgentMessagesLoaded -> THREAD_LOADED
|
|
74
|
-
AssistantThread.THREAD_FAILED -> REPLY_FAILED
|
|
75
|
-
AssistantLoop.STEP -> STEP
|
|
76
|
-
AssistantLoop.REPLIED -> REPLIED
|
|
77
|
-
AssistantLoop.REPLY_FAILED -> REPLY_FAILED
|
|
78
|
-
}
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
|
|
93
|
+
;; The full assistant page: the same conversation as the side panel, in the
|
|
94
|
+
;; app's own shell.
|
|
95
|
+
trait AssistantLayout = Shell.traits.AppLayout {
|
|
82
96
|
config {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
97
|
+
viewerRole: @config.viewerRole
|
|
98
|
+
viewerName: @user.name
|
|
99
|
+
viewerEmail: @user.email
|
|
100
|
+
appName: @config.appName
|
|
101
|
+
navItems: @config.navItems
|
|
102
|
+
contentTrait: <trait.AssistantConversation />
|
|
103
|
+
contentClassName: "mx-auto flex w-full max-w-3xl flex-col h-[calc(100vh-9rem)] min-h-0"
|
|
87
104
|
}
|
|
88
105
|
}
|
|
89
106
|
|
|
@@ -102,7 +119,8 @@ orbital AgentAssistantOrbital {
|
|
|
102
119
|
}
|
|
103
120
|
listens {
|
|
104
121
|
AssistantContext.COMPACTED -> RECALL
|
|
105
|
-
AssistantLoop.
|
|
122
|
+
AssistantLoop.ASSISTANT_REPLIED -> REMEMBER
|
|
123
|
+
*.ASSISTANT_MEMORIES_REQUESTED -> LIST
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
126
|
|
|
@@ -119,19 +137,18 @@ orbital AgentAssistantOrbital {
|
|
|
119
137
|
|
|
120
138
|
trait AssistantTrace = Trace.traits.AgentTraceRecorder -> AgentRun {
|
|
121
139
|
listens {
|
|
122
|
-
AssistantLoop.
|
|
123
|
-
AssistantLoop.
|
|
140
|
+
AssistantLoop.ASSISTANT_REPLIED -> REPLIED
|
|
141
|
+
AssistantLoop.ASSISTANT_REPLY_FAILED -> REPLY_FAILED
|
|
124
142
|
}
|
|
125
143
|
}
|
|
126
144
|
|
|
127
|
-
page "/assistant" as AssistantPage ->
|
|
145
|
+
page "/assistant" as AssistantPage -> AssistantLayout, AssistantThread, AssistantChatKeeper, AssistantContext, AssistantMemory, AssistantLoop, AssistantTrace
|
|
128
146
|
}
|
|
129
147
|
|
|
130
148
|
;; The assistant's records live outside AgentAssistantOrbital, so a consumer
|
|
131
149
|
;; importing that orbital retargets them onto its own owner-scoped entities:
|
|
132
150
|
;; `entities { AgentMessage: MyMessage, AgentMemory: MyMemory, AgentRun: MyRun }`.
|
|
133
151
|
orbital AgentRecordsOrbital {
|
|
134
|
-
uses Memory from "std/behaviors/std-agent-memory"
|
|
135
152
|
uses Trace from "std/behaviors/std-agent-trace"
|
|
136
153
|
|
|
137
154
|
entity AgentRun [persistent: agentRuns] {
|
|
@@ -143,6 +160,15 @@ orbital AgentRecordsOrbital {
|
|
|
143
160
|
entity AgentMemory [persistent: agentMemories] {
|
|
144
161
|
id : string!
|
|
145
162
|
}
|
|
163
|
+
entity AgentChat [persistent: agentChats] {
|
|
164
|
+
id : string!
|
|
165
|
+
ownerId : string!
|
|
166
|
+
title : string = ""
|
|
167
|
+
startedAt : number = 0
|
|
168
|
+
lastActiveAt : number = 0
|
|
169
|
+
pendingMessage : string @intrinsic = ""
|
|
170
|
+
pendingReply : string @intrinsic = ""
|
|
171
|
+
}
|
|
146
172
|
|
|
147
173
|
trait AgentRunHistory = Trace.traits.AgentRunBrowse -> AgentRun {
|
|
148
174
|
listens {
|
|
@@ -150,12 +176,5 @@ orbital AgentRecordsOrbital {
|
|
|
150
176
|
}
|
|
151
177
|
}
|
|
152
178
|
|
|
153
|
-
trait AgentMemoryList = Memory.traits.AgentMemoryBrowse -> AgentMemory {
|
|
154
|
-
listens {
|
|
155
|
-
AgentAssistantOrbital.AssistantMemory.MEMORY_STORED -> MEMORY_STORED
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
179
|
page "/assistant/runs" as AssistantRunsPage -> AgentRunHistory
|
|
160
|
-
page "/assistant/memory" as AssistantMemoryPage -> AgentMemoryList
|
|
161
180
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
app std-page-open "1.0.0"
|
|
2
|
+
"std-page-open — opens a URL in a new, focused browser tab and reports the tab. Wire OPEN to something the person does (a button or a row action), so a page only ever opens because they asked for it. Needs a host with the `page` service (a browser extension)."
|
|
3
|
+
|
|
4
|
+
orbital PageOpenOrbital {
|
|
5
|
+
entity PageOpenRecord [runtime] {
|
|
6
|
+
opened : number @description "How many pages were opened" @synonyms "count" = 0
|
|
7
|
+
errorMessage : string @description "Why the last page could not be opened; blank = none" @synonyms "error, failure reason" = ""
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
trait PageOpen -> PageOpenRecord [interaction, instance] {
|
|
11
|
+
initial: ready
|
|
12
|
+
|
|
13
|
+
config {
|
|
14
|
+
statusSlot : slot = main
|
|
15
|
+
@label "Status slot"
|
|
16
|
+
@description "Where the atom shows its one-line status; an organism running it in the background sends it to `system`"
|
|
17
|
+
@synonyms "status area, slot"
|
|
18
|
+
@tier "presentation"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
state ready {
|
|
22
|
+
INIT -> ready
|
|
23
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: "Ready to open pages" })
|
|
24
|
+
OPEN -> ready
|
|
25
|
+
(call-service "page" "openTab" { url: ?url, active: true }
|
|
26
|
+
{ emit: { success: TAB_OPENED, failure: OPEN_CALL_FAILED } })
|
|
27
|
+
TAB_OPENED -> ready
|
|
28
|
+
(set @entity.opened (+ @entity.opened 1))
|
|
29
|
+
(emit OPENED { tab: ?tab })
|
|
30
|
+
OPEN_CALL_FAILED -> ready
|
|
31
|
+
(set @entity.errorMessage ?error)
|
|
32
|
+
(emit OPEN_FAILED { error: ?error })
|
|
33
|
+
(notify error (str/concat "Couldn't open the page — " ?error))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
emits {
|
|
37
|
+
TAB_OPENED { tab : number! }
|
|
38
|
+
@description "The page service opened the tab"
|
|
39
|
+
@tier "secondary"
|
|
40
|
+
OPEN_CALL_FAILED { error : string! }
|
|
41
|
+
@description "The page service could not open the tab"
|
|
42
|
+
@tier "secondary"
|
|
43
|
+
OPENED -> external { tab : number! }
|
|
44
|
+
@description "The page is open in a new, focused tab"
|
|
45
|
+
@synonyms "opened, launched, visited"
|
|
46
|
+
@tier "primary"
|
|
47
|
+
OPEN_FAILED -> external { error : string! }
|
|
48
|
+
@description "The page could not be opened"
|
|
49
|
+
@tier "primary"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
listens {
|
|
53
|
+
OPEN -> external { url : string! }
|
|
54
|
+
@description "Open `url` in a new, focused tab — wire it to something the person does"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
page "/page-open" as PageOpenPage -> PageOpen
|
|
59
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
app std-page-search "1.0.0"
|
|
2
|
+
"std-page-search — runs one search on a site in a background tab: opens `searchUrl` with the query as the `queryParam` parameter, reads up to `maxResults` elements matching `resultSelector` (waiting up to `waitMs` for the results to appear), closes the tab, and reports RESULTS with the fields read from each. One search at a time: SEARCH is accepted again once RESULTS or SEARCH_FAILED is reported. It never clicks anything. Needs a host with the `page` service (a browser extension with access to the search pages)."
|
|
3
|
+
|
|
4
|
+
orbital PageSearchOrbital {
|
|
5
|
+
type PageField = {
|
|
6
|
+
selector : string
|
|
7
|
+
attr : string
|
|
8
|
+
resolve : boolean
|
|
9
|
+
template : string
|
|
10
|
+
required : boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type PageResult = {
|
|
14
|
+
elementId : string!
|
|
15
|
+
fields : Map string string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
entity PageSearchRecord [runtime] {
|
|
19
|
+
query : string @description "The search being run" @synonyms "search term, terms, topic" = ""
|
|
20
|
+
tab : number @description "The background tab running the search; 0 = none" @synonyms "tab id" = 0
|
|
21
|
+
errorMessage : string @description "Why the last search failed; blank = none" @synonyms "error, failure reason" = ""
|
|
22
|
+
status : "idle" | "searching" @description "Whether a search is running" @synonyms "state, busy" = idle
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
trait PageSearch -> PageSearchRecord [interaction, instance] {
|
|
26
|
+
initial: idle
|
|
27
|
+
|
|
28
|
+
config {
|
|
29
|
+
statusSlot : slot = main
|
|
30
|
+
@label "Status slot"
|
|
31
|
+
@description "Where the atom shows its one-line status; an organism running it in the background sends it to `system`"
|
|
32
|
+
@synonyms "status area, slot"
|
|
33
|
+
@tier "presentation"
|
|
34
|
+
searchUrl : string = "https://example.com/search"
|
|
35
|
+
@label "Search page"
|
|
36
|
+
@description "URL of the site's search results page, without the query"
|
|
37
|
+
@synonyms "search url, results page, endpoint"
|
|
38
|
+
@tier "domain"
|
|
39
|
+
resultsMatch : string = "https://example.com/search*"
|
|
40
|
+
@label "Results pages"
|
|
41
|
+
@description "URL match pattern the results page lies in (Chrome match-pattern syntax); the search page must match it, and the extension asks for access to exactly these pages"
|
|
42
|
+
@synonyms "site, url pattern, results url"
|
|
43
|
+
@tier "domain"
|
|
44
|
+
queryParam : string = "q"
|
|
45
|
+
@label "Query parameter"
|
|
46
|
+
@description "Name of the URL parameter that carries the search terms"
|
|
47
|
+
@synonyms "query key, search parameter"
|
|
48
|
+
@tier "domain"
|
|
49
|
+
resultSelector : string = ".result"
|
|
50
|
+
@label "Result selector"
|
|
51
|
+
@description "CSS selector of one result on the results page"
|
|
52
|
+
@synonyms "result, item, card, tile"
|
|
53
|
+
@tier "domain"
|
|
54
|
+
fields : Map string PageField = {}
|
|
55
|
+
@label "Fields"
|
|
56
|
+
@description "Field name to how to read it from each result: `selector` (a descendant; absent reads the result itself), `attr` (an attribute; absent reads the text) , `resolve` (the value is a URL: return it absolute), `template` (compose the value from other fields, `{name}` per field; empty when any is empty) and `required` (leave the result out until this field reads non-empty)"
|
|
57
|
+
@synonyms "columns, properties, extract"
|
|
58
|
+
@tier "domain"
|
|
59
|
+
maxResults : number = 10
|
|
60
|
+
@label "Results per search"
|
|
61
|
+
@description "Most results read from one search"
|
|
62
|
+
@synonyms "limit, count, how many"
|
|
63
|
+
@tier "policy"
|
|
64
|
+
waitMs : number = 8000
|
|
65
|
+
@label "Wait for results (ms)"
|
|
66
|
+
@description "How long to wait for the results page to load and its first result to appear"
|
|
67
|
+
@synonyms "timeout, wait, patience"
|
|
68
|
+
@tier "infra"
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
state idle {
|
|
72
|
+
INIT -> idle
|
|
73
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: "Ready to search" })
|
|
74
|
+
SEARCH -> searching
|
|
75
|
+
(set @entity.query ?query)
|
|
76
|
+
(set @entity.tab 0)
|
|
77
|
+
(set @entity.status searching)
|
|
78
|
+
(call-service "page" "openTab" { url: @config.searchUrl, params: (object/set {} @config.queryParam ?query), match: @config.resultsMatch, active: false }
|
|
79
|
+
{ emit: { success: TAB_OPENED, failure: SEARCH_CALL_FAILED } })
|
|
80
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Searching for " ?query) })
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
state searching {
|
|
84
|
+
INIT -> searching
|
|
85
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Searching for " @entity.query) })
|
|
86
|
+
TAB_OPENED -> searching
|
|
87
|
+
(set @entity.tab ?tab)
|
|
88
|
+
(call-service "page" "query" { tab: ?tab, selector: @config.resultSelector, fields: @config.fields, limit: @config.maxResults, waitMs: @config.waitMs }
|
|
89
|
+
{ emit: { success: RESULTS_READ, failure: SEARCH_CALL_FAILED } })
|
|
90
|
+
RESULTS_READ -> idle
|
|
91
|
+
(call-service "page" "closeTab" { tab: ?tab })
|
|
92
|
+
(set @entity.tab 0)
|
|
93
|
+
(set @entity.status idle)
|
|
94
|
+
(emit RESULTS { query: @entity.query, results: ?elements })
|
|
95
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Searched for " @entity.query) })
|
|
96
|
+
SEARCH_CALL_FAILED -> idle when (> @entity.tab 0)
|
|
97
|
+
(call-service "page" "closeTab" { tab: @entity.tab })
|
|
98
|
+
(set @entity.tab 0)
|
|
99
|
+
(set @entity.errorMessage ?error)
|
|
100
|
+
(set @entity.status idle)
|
|
101
|
+
(emit SEARCH_FAILED { query: @entity.query, error: ?error })
|
|
102
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Couldn't search: " ?error) })
|
|
103
|
+
SEARCH_CALL_FAILED -> idle when (== @entity.tab 0)
|
|
104
|
+
(set @entity.errorMessage ?error)
|
|
105
|
+
(set @entity.status idle)
|
|
106
|
+
(emit SEARCH_FAILED { query: @entity.query, error: ?error })
|
|
107
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Couldn't search: " ?error) })
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
emits {
|
|
111
|
+
TAB_OPENED { tab : number! }
|
|
112
|
+
@description "The background tab for the search is open"
|
|
113
|
+
@tier "secondary"
|
|
114
|
+
RESULTS_READ { tab : number!, elements : [PageResult]! }
|
|
115
|
+
@description "The results were read from the search page"
|
|
116
|
+
@tier "secondary"
|
|
117
|
+
SEARCH_CALL_FAILED { error : string! }
|
|
118
|
+
@description "Opening the search page or reading it failed"
|
|
119
|
+
@tier "secondary"
|
|
120
|
+
RESULTS -> external { query : string!, results : [PageResult]! }
|
|
121
|
+
@description "What one search found: each result's element id and the fields read from it"
|
|
122
|
+
@synonyms "search results, found, hits"
|
|
123
|
+
@tier "primary"
|
|
124
|
+
SEARCH_FAILED -> external { query : string!, error : string! }
|
|
125
|
+
@description "The search could not be run or read"
|
|
126
|
+
@tier "primary"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
listens {
|
|
130
|
+
SEARCH -> external { query : string! }
|
|
131
|
+
@description "Search the site for `query`"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
page "/page-search" as PageSearchPage -> PageSearch
|
|
136
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
app std-page-treat "1.0.0"
|
|
2
|
+
"std-page-treat — applies a declared visual treatment to one element of a browser tab (hide, blur, highlight, or label it with text) or restores it, and reports which element it treated. TREAT takes the tab and element id std-page-watch reports. The treatments are a fixed, declared set: no arbitrary styles or scripts. Needs a host with the `page` service (a browser extension)."
|
|
3
|
+
|
|
4
|
+
orbital PageTreatOrbital {
|
|
5
|
+
entity PageTreatRecord [runtime] {
|
|
6
|
+
treated : number @description "How many treatments were applied" @synonyms "count, applied" = 0
|
|
7
|
+
errorMessage : string @description "Why the last treatment failed; blank = none" @synonyms "error, failure reason" = ""
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
trait PageTreat -> PageTreatRecord [interaction, instance] {
|
|
11
|
+
initial: ready
|
|
12
|
+
|
|
13
|
+
config {
|
|
14
|
+
statusSlot : slot = main
|
|
15
|
+
@label "Status slot"
|
|
16
|
+
@description "Where the atom shows its one-line status; an organism running it in the background sends it to `system`"
|
|
17
|
+
@synonyms "status area, slot"
|
|
18
|
+
@tier "presentation"
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
state ready {
|
|
22
|
+
INIT -> ready
|
|
23
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: "Ready to treat page elements" })
|
|
24
|
+
TREAT -> ready
|
|
25
|
+
(call-service "page" "apply" { tab: ?tab, elementId: ?elementId, treatment: ?treatment, label: ?label }
|
|
26
|
+
{ emit: { success: APPLIED, failure: APPLY_FAILED } })
|
|
27
|
+
APPLIED -> ready
|
|
28
|
+
(set @entity.treated (+ @entity.treated 1))
|
|
29
|
+
(emit TREATED { tab: ?tab, elementId: ?elementId, treatment: ?treatment })
|
|
30
|
+
APPLY_FAILED -> ready
|
|
31
|
+
(set @entity.errorMessage ?error)
|
|
32
|
+
(emit TREAT_FAILED { error: ?error })
|
|
33
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Couldn't treat a page element: " ?error) })
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
emits {
|
|
37
|
+
APPLIED { applied : boolean!, tab : number!, elementId : string!, treatment : "hide" | "blur" | "highlight" | "label" | "restore" }
|
|
38
|
+
@description "The page service applied the treatment"
|
|
39
|
+
@tier "secondary"
|
|
40
|
+
APPLY_FAILED { error : string! }
|
|
41
|
+
@description "The page service could not apply the treatment"
|
|
42
|
+
@tier "secondary"
|
|
43
|
+
TREATED -> external { tab : number!, elementId : string!, treatment : "hide" | "blur" | "highlight" | "label" | "restore" }
|
|
44
|
+
@description "The element now shows the treatment (or, for restore, is back as it was)"
|
|
45
|
+
@synonyms "hidden, blurred, highlighted, labelled, restored"
|
|
46
|
+
@tier "primary"
|
|
47
|
+
TREAT_FAILED -> external { error : string! }
|
|
48
|
+
@description "The treatment could not be applied (the element is gone, or the tab closed)"
|
|
49
|
+
@tier "primary"
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
listens {
|
|
53
|
+
TREAT -> external { tab : number!, elementId : string!, treatment : "hide" | "blur" | "highlight" | "label" | "restore", label : string }
|
|
54
|
+
@description "Treat one page element; `label` is the text shown with it (for example why it was hidden)"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
page "/page-treat" as PageTreatPage -> PageTreat
|
|
59
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
app std-page-watch "1.0.0"
|
|
2
|
+
"std-page-watch — watches every browser tab whose URL matches `match` for elements matching `itemSelector`, and reports each element once as ITEM with the `fields` read from it, now and as the page changes. Needs a host with the `page` service (a browser extension, which asks for access to exactly the `match` pages). Each item carries its tab and element id; compose std-page-treat to hide, blur, highlight or label it."
|
|
3
|
+
|
|
4
|
+
orbital PageWatchOrbital {
|
|
5
|
+
type PageField = {
|
|
6
|
+
selector : string
|
|
7
|
+
attr : string
|
|
8
|
+
resolve : boolean
|
|
9
|
+
template : string
|
|
10
|
+
required : boolean
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
entity PageWatchRecord [runtime] {
|
|
14
|
+
watchId : string @description "Id of the attached watch" @synonyms "watch id, subscription" = ""
|
|
15
|
+
found : number @description "How many elements have been reported" @synonyms "count, items seen, reported" = 0
|
|
16
|
+
errorMessage : string @description "Why the watch could not be attached; blank = none" @synonyms "error, failure reason" = ""
|
|
17
|
+
status : "starting" | "watching" | "failed" @description "Whether the watch is attached" @synonyms "state, watching, connected" = starting
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
trait PageWatch -> PageWatchRecord [lifecycle, instance] {
|
|
21
|
+
initial: active
|
|
22
|
+
|
|
23
|
+
config {
|
|
24
|
+
statusSlot : slot = main
|
|
25
|
+
@label "Status slot"
|
|
26
|
+
@description "Where the atom shows its one-line status; an organism running it in the background sends it to `system`"
|
|
27
|
+
@synonyms "status area, slot"
|
|
28
|
+
@tier "presentation"
|
|
29
|
+
match : string = "https://example.com/*"
|
|
30
|
+
@label "Pages"
|
|
31
|
+
@description "URL match pattern of the tabs to watch, in Chrome match-pattern syntax (`<scheme>://<host>/<path>` with `*` wildcards)"
|
|
32
|
+
@synonyms "site, url pattern, pages, domain"
|
|
33
|
+
@tier "domain"
|
|
34
|
+
itemSelector : string = ".item"
|
|
35
|
+
@label "Item selector"
|
|
36
|
+
@description "CSS selector of the elements to report"
|
|
37
|
+
@synonyms "selector, element, card, tile, row"
|
|
38
|
+
@tier "domain"
|
|
39
|
+
fields : Map string PageField = {}
|
|
40
|
+
@label "Fields"
|
|
41
|
+
@description "Field name to how to read it from each element: `selector` (a descendant; absent reads the element itself), `attr` (an attribute; absent reads the text) , `resolve` (the value is a URL: return it absolute), `template` (compose the value from other fields, `{name}` per field; empty when any is empty) and `required` (leave the element out until this field reads non-empty — a placeholder is reported once it fills in)"
|
|
42
|
+
@synonyms "columns, properties, extract, read"
|
|
43
|
+
@tier "domain"
|
|
44
|
+
itemEvent : event = "PageWatch.ITEM_FOUND"
|
|
45
|
+
@label "Item input"
|
|
46
|
+
@description "The declared input each element arrives as; it follows the trait when a composer renames it"
|
|
47
|
+
@tier "internal"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
state active {
|
|
51
|
+
INIT -> active
|
|
52
|
+
(call-service "page" "watch" { match: @config.match, selector: @config.itemSelector, fields: @config.fields, event: @config.itemEvent }
|
|
53
|
+
{ emit: { success: WATCHING, failure: WATCH_FAILED } })
|
|
54
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: "Connecting to the pages…" })
|
|
55
|
+
WATCHING -> active
|
|
56
|
+
(set @entity.watchId ?watchId)
|
|
57
|
+
(set @entity.status watching)
|
|
58
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Watching " @config.match) })
|
|
59
|
+
WATCH_FAILED -> active
|
|
60
|
+
(set @entity.errorMessage ?error)
|
|
61
|
+
(set @entity.status failed)
|
|
62
|
+
(render-ui @config.statusSlot { type: typography, variant: body, role: status, content: (str/concat "Can't watch these pages: " ?error) })
|
|
63
|
+
ITEM_FOUND -> active
|
|
64
|
+
(set @entity.found (+ @entity.found 1))
|
|
65
|
+
(emit ITEM { tab: ?tab, elementId: ?elementId, url: ?url, fields: ?fields })
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
emits {
|
|
69
|
+
WATCHING { watchId : string! }
|
|
70
|
+
@description "The watch is attached"
|
|
71
|
+
@tier "secondary"
|
|
72
|
+
WATCH_FAILED -> external { error : string! }
|
|
73
|
+
@description "The watch could not be attached (no page service, or no access to the pages)"
|
|
74
|
+
@tier "primary"
|
|
75
|
+
ITEM -> external { tab : number!, elementId : string!, url : string!, fields : Map string string }
|
|
76
|
+
@description "One matching element, reported once, with the fields read from it"
|
|
77
|
+
@synonyms "element found, item seen, new item"
|
|
78
|
+
@tier "primary"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
listens {
|
|
82
|
+
ITEM_FOUND -> external { tab : number!, elementId : string!, url : string!, fields : Map string string }
|
|
83
|
+
@description "One matching element, delivered by the page service"
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
page "/page-watch" as PageWatchPage -> PageWatch
|
|
88
|
+
}
|
|
@@ -17,8 +17,16 @@ orbital BookReaderOrbital {
|
|
|
17
17
|
initial: cover
|
|
18
18
|
|
|
19
19
|
state cover {
|
|
20
|
-
INIT -> cover
|
|
20
|
+
INIT -> cover when (== @config.opensAt "cover")
|
|
21
21
|
(render-ui main @config.coverContent)
|
|
22
|
+
INIT -> reading when (== @config.opensAt "reading")
|
|
23
|
+
(set @entity.chapterIndex 0)
|
|
24
|
+
(set @entity.chapterTitle (object/get (array/nth @config.chapters 0) title ""))
|
|
25
|
+
(set @entity.chapterContent (object/get (array/nth @config.chapters 0) content ""))
|
|
26
|
+
(set @entity.isFirst true)
|
|
27
|
+
(set @entity.isLast (<= (array/len @config.chapters) 1))
|
|
28
|
+
(set @entity.progressLabel (str/concat "1 / " (array/len @config.chapters)))
|
|
29
|
+
(render-ui main @config.readingContent)
|
|
22
30
|
START_READING -> reading
|
|
23
31
|
(set @entity.chapterIndex 0)
|
|
24
32
|
(set @entity.chapterTitle (object/get (array/nth @config.chapters 0) title ""))
|
|
@@ -92,6 +100,10 @@ orbital BookReaderOrbital {
|
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
config {
|
|
103
|
+
opensAt : "cover" | "reading" = "cover"
|
|
104
|
+
@label "Opens at"
|
|
105
|
+
@description "Where the reader opens: the cover, or straight into the first chapter (an article read in place)"
|
|
106
|
+
@tier "presentation"
|
|
95
107
|
title : string = "Untitled book"
|
|
96
108
|
@label "Title"
|
|
97
109
|
@tier "presentation"
|
|
@@ -236,6 +248,18 @@ orbital BookReaderOrbital {
|
|
|
236
248
|
align: start
|
|
237
249
|
className: "lg:sticky lg:top-6 self-start"
|
|
238
250
|
children: [{
|
|
251
|
+
type: typography
|
|
252
|
+
content: @config.title
|
|
253
|
+
variant: h5
|
|
254
|
+
weight: semibold
|
|
255
|
+
className: "text-balance"
|
|
256
|
+
}, {
|
|
257
|
+
type: typography
|
|
258
|
+
content: @config.author
|
|
259
|
+
variant: caption
|
|
260
|
+
color: muted
|
|
261
|
+
className: (if (== @config.author "") hidden "pb-2")
|
|
262
|
+
}, {
|
|
239
263
|
type: button
|
|
240
264
|
variant: link
|
|
241
265
|
label: @config.coverLabel
|
|
@@ -85,6 +85,15 @@ orbital CardGridOrbital {
|
|
|
85
85
|
variant: h4
|
|
86
86
|
weight: semibold
|
|
87
87
|
align: (if (== @config.look profile) center left)
|
|
88
|
+
className: (if (== (object/get @item href "") "") "" hidden)
|
|
89
|
+
}, {
|
|
90
|
+
type: button
|
|
91
|
+
label: (object/get @item title "")
|
|
92
|
+
href: (object/get @item href "")
|
|
93
|
+
variant: link
|
|
94
|
+
action: CARD_OPEN
|
|
95
|
+
actionPayload: { href: (object/get @item href ""), title: (object/get @item title "") }
|
|
96
|
+
className: (if (== (object/get @item href "") "") hidden "text-lg font-semibold text-foreground whitespace-normal text-start")
|
|
88
97
|
}, {
|
|
89
98
|
type: typography
|
|
90
99
|
content: (object/get @item meta "")
|