isorite 0.3.0 → 0.5.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/README.md +3 -3
- package/dist/core.js +4 -2
- package/dist/embed-worker.js +9 -4
- package/dist/main.js +7707 -7203
- package/docs/setup-for-agents.md +27 -2
- package/docs/setup.md +28 -2
- package/docs/terminal.md +16 -4
- package/package.json +1 -1
- package/plugin/tui.tsx +3 -77
- package/web/_app/immutable/assets/{0.IOi79DXH.css → 0.DNvf5eFW.css} +1 -1
- package/web/_app/immutable/chunks/-7PsQOY4.js +3 -0
- package/web/_app/immutable/chunks/B9vLu37F.js +1 -0
- package/web/_app/immutable/chunks/BAVeNK5c.js +20 -0
- package/web/_app/immutable/chunks/C1xA2mdF.js +1 -0
- package/web/_app/immutable/chunks/CQqNkDRK.js +1 -0
- package/web/_app/immutable/chunks/Cs9sQ8Oa.js +1 -0
- package/web/_app/immutable/chunks/{BNCmWi9e.js → D4Jg9Smi.js} +1 -1
- package/web/_app/immutable/chunks/{BN1DRYj2.js → DG2llOXe.js} +21 -21
- package/web/_app/immutable/chunks/DqVFJWdL.js +1 -0
- package/web/_app/immutable/chunks/{BZeALCyJ.js → HCrXwzvD.js} +1 -1
- package/web/_app/immutable/chunks/MjV3DsQW.js +1 -0
- package/web/_app/immutable/chunks/uID4v_1K.js +1 -0
- package/web/_app/immutable/entry/app.CR9ubCCo.js +2 -0
- package/web/_app/immutable/entry/start.D4DOZCP6.js +1 -0
- package/web/_app/immutable/nodes/0.BYXv43VP.js +26 -0
- package/web/_app/immutable/nodes/1.DGP71l35.js +1 -0
- package/web/_app/immutable/nodes/2.CxspTYRG.js +1 -0
- package/web/_app/immutable/nodes/3.ZTzs6oEI.js +1 -0
- package/web/_app/immutable/nodes/4.CeIGu2c2.js +2 -0
- package/web/_app/immutable/nodes/5.k1DmYkGm.js +5 -0
- package/web/_app/immutable/nodes/6.Q0yGno8p.js +1 -0
- package/web/_app/immutable/nodes/7.BJRu312o.js +1 -0
- package/web/_app/version.json +1 -1
- package/web/index.html +17 -20
- package/dist/tutorial/barnes_hut.py +0 -46
- package/dist/tutorial/sim.py +0 -29
- package/plugin/tour.ts +0 -74
- package/web/_app/immutable/assets/8.CFA9_nYZ.css +0 -1
- package/web/_app/immutable/assets/Tutorial.DOfsM49T.css +0 -1
- package/web/_app/immutable/chunks/-XH9wLvF.js +0 -1
- package/web/_app/immutable/chunks/B_AYGp9o.js +0 -1
- package/web/_app/immutable/chunks/BavLcJN1.js +0 -1
- package/web/_app/immutable/chunks/BcrJkegU.js +0 -1
- package/web/_app/immutable/chunks/BqrPWk6N.js +0 -20
- package/web/_app/immutable/chunks/Bze-M_kr.js +0 -3
- package/web/_app/immutable/chunks/CRzLMJPS.js +0 -1
- package/web/_app/immutable/chunks/Cr4-_2Vw.js +0 -1
- package/web/_app/immutable/chunks/D25FgEb3.js +0 -43
- package/web/_app/immutable/chunks/D8jeeZa0.js +0 -1
- package/web/_app/immutable/chunks/DHMLhlug.js +0 -1
- package/web/_app/immutable/chunks/DsnHcTIO.js +0 -1
- package/web/_app/immutable/entry/app.B9MdwZzs.js +0 -2
- package/web/_app/immutable/entry/start.gtQcHKDR.js +0 -1
- package/web/_app/immutable/nodes/0.DSajjyn5.js +0 -15
- package/web/_app/immutable/nodes/1.hmQ_l8t-.js +0 -1
- package/web/_app/immutable/nodes/2.Bu6Zvx8F.js +0 -1
- package/web/_app/immutable/nodes/3.DcSG1nNk.js +0 -1
- package/web/_app/immutable/nodes/4.QUhLgH4i.js +0 -2
- package/web/_app/immutable/nodes/5.D3JSWVyR.js +0 -5
- package/web/_app/immutable/nodes/6.CWfUNDww.js +0 -1
- package/web/_app/immutable/nodes/7.DmPPLDvb.js +0 -1
- package/web/_app/immutable/nodes/8.CwMTBog4.js +0 -486
package/docs/setup-for-agents.md
CHANGED
|
@@ -14,12 +14,13 @@ turn off certificate checking (`NODE_TLS_REJECT_UNAUTHORIZED=0`).
|
|
|
14
14
|
|
|
15
15
|
| Command | What it does |
|
|
16
16
|
|---|---|
|
|
17
|
+
| `isorite new <folder>` | Makes the folder (and its parents), empty but for `.isorite/`. Refuses one with files in it already: that is opened as it is. |
|
|
17
18
|
| `isorite doctor [<notes>] [--report] [--offline]` | Checks the setup a step at a time (below). Exit 1 if anything failed. `--offline`: asks the provider nothing. |
|
|
18
19
|
| `isorite serve <notes> [--port <n>]` | The browser app at `http://localhost:4767/`, on 127.0.0.1 only. |
|
|
19
20
|
| `isorite term [<notes>] [--port <n>]` | OpenCode 2 in this terminal, in the current folder (the code), with the notes beside it. Starts the isorite server if none answers on the port, and stops it on exit. |
|
|
20
21
|
| `isorite check <notes>` | Lints the notes: frontmatter, links, cycles. |
|
|
21
|
-
| `isorite
|
|
22
|
-
| `isorite model [--from <folder>]` | Optional:
|
|
22
|
+
| `isorite guide [<folder>]` | The guide, a knowledge base about isorite, written into `~/isorite-guide` (or the folder named) and served. With no provider set up, Iso is the guide's finder: no key or network needed. Good to check the install alone. `isorite --tutorial` is another name for it. |
|
|
23
|
+
| `isorite model [--from <folder>]` | Optional: the model for search by meaning. Says which is installed and where, else fetches it (53 MB) from npm, a GitHub release, then Hugging Face, saying why each failed. `--from`: granite's files, or any compatible model (below). |
|
|
23
24
|
| `isorite help setup` | Prints this. |
|
|
24
25
|
|
|
25
26
|
## Files
|
|
@@ -29,7 +30,10 @@ turn off certificate checking (`NODE_TLS_REJECT_UNAUTHORIZED=0`).
|
|
|
29
30
|
| `~/.config/isorite/config.toml` (or `$XDG_CONFIG_HOME/isorite/`) | isorite's settings for this machine: `device`, `[provider]`, `[tiers]`, `[prices]`. Mode 600. Holds the key. |
|
|
30
31
|
| `~/.config/isorite/opencode/` | OpenCode's settings as isorite writes them at each `isorite term` start (`opencode.json`, theme, plugin link). Rewritten; don't edit. |
|
|
31
32
|
| `~/.config/isorite/opencode/local.json` | The person's own OpenCode settings, laid over isorite's. Never rewritten. |
|
|
33
|
+
| `<notes>/AGENTS.md` | The base's own instructions (notation, level, how notes are named), sent to Iso with every turn and named in OpenCode's `instructions`. Not a note: not on the Atlas, not linted. |
|
|
32
34
|
| `<notes>/.isorite/` | isorite's own folder in the notes: journals of every change, `settings.toml` (`repository = "<path>"`, where code links point). Made on first open; commit it with the notes. |
|
|
35
|
+
| `~/.local/share/isorite/models/` (or `$XDG_DATA_HOME/isorite/models/`) | The models for search by meaning, a folder each: `granite-embedding-small-english-r2/`, and any other installed with `--from`. `use` names the one used, if not granite. |
|
|
36
|
+
| `~/.cache/isorite/vectors-<hash>.json` | Each base's vectors, made by the model; made again when the model changes. |
|
|
33
37
|
| `$TMPDIR/isorite-term-<port>.log` | The isorite server's output, when `isorite term` started it. Look here when "the isorite server did not start". |
|
|
34
38
|
| `$TMPDIR/isorite-opencode-<port>.log` | OpenCode's server's output, under `isorite term`. |
|
|
35
39
|
|
|
@@ -44,6 +48,7 @@ url = "https://…/v1" # OpenAI-compatible; /chat/completions a
|
|
|
44
48
|
key = "…" # the key; also settable in the browser (behind the logo)
|
|
45
49
|
key_header = "api-key" # send the key in this header, as it is, instead of Authorization: Bearer
|
|
46
50
|
stream_usage = false # don't send stream_options (some gateways answer 400 to it)
|
|
51
|
+
max_tokens = 16000 # the least output budget any call gets, for models that think at length
|
|
47
52
|
cache_marks = true # send Anthropic cache marks (OpenRouter-style gateways only)
|
|
48
53
|
pfx = "~/certs/me.pfx" # client certificate (mTLS), PKCS#12; or cert + cert_key (PEM)
|
|
49
54
|
pfx_passphrase = "$PFX_PASS" # "$NAME": read from that environment variable
|
|
@@ -82,6 +87,8 @@ Rules:
|
|
|
82
87
|
| `ISORITE_NODE_DEFAULTS=0` | Leave Node as it is: no system authorities, no proxy unless Node is told. |
|
|
83
88
|
| `NODE_EXTRA_CA_CERTS` | A .pem of extra authorities, read by Node at start. |
|
|
84
89
|
| `ISORITE_GATEWAY_URL`, `ISORITE_GATEWAY_MODELS`, `ISORITE_GATEWAY_KEY` | The terminal only: OpenCode asks this OpenAI-compatible address directly, with these models (comma-separated) and key, instead of `config.toml`'s provider. No certificate or headers apply. |
|
|
90
|
+
| `ISORITE_EMBED_DIR` | The model for search by meaning is in this folder (and nowhere else is looked). |
|
|
91
|
+
| `ISORITE_EMBED=off` | No search by meaning, whatever is installed. |
|
|
85
92
|
| `ISORITE_OPENCODE` | The OpenCode 2 binary to run. Otherwise `opencode2`, then `opencode`, must be version 2. |
|
|
86
93
|
| `XDG_CONFIG_HOME` | Where `isorite/config.toml` is looked for. |
|
|
87
94
|
| `SSH_CONNECTION` | Set over SSH: `term`, `serve` and `doctor` print the `ssh -L` command for the browser. |
|
|
@@ -96,6 +103,12 @@ Rules:
|
|
|
96
103
|
server's relay, and with the server's token as its key. The relay adds the real key, certificate, headers and
|
|
97
104
|
query, and passes `/chat/completions` and `/models` on. So whatever works for Iso works for the terminal. The
|
|
98
105
|
relay answers only on 127.0.0.1, and only that token.
|
|
106
|
+
- **Retries.** A 502, 503 or 504, a dropped connection or a timeout is asked again up to 3 times (after about 1, 2
|
|
107
|
+
and 4 s), and a 429 once (after its `Retry-After`, if 10 s or less), but only before any of the reply has come.
|
|
108
|
+
The relay does the same before it passes anything to OpenCode (which retries on its own as well). 4xx answers
|
|
109
|
+
and names not found are the settings', and are not asked again.
|
|
110
|
+
- **An empty reply.** A reply cut off at its length limit with nothing written (a reasoning model that spent the
|
|
111
|
+
budget thinking) is asked once more with three times the room.
|
|
99
112
|
- With no key and no certificate in `config.toml`, and no `ISORITE_GATEWAY_*`, OpenCode uses its own models.
|
|
100
113
|
`isorite term` prints which models it uses, and from where.
|
|
101
114
|
|
|
@@ -131,6 +144,10 @@ Rules:
|
|
|
131
144
|
| `ETIMEDOUT`, `UND_ERR_CONNECT_TIMEOUT` | No answer: often a proxy is needed. | Set `HTTPS_PROXY` (and `NO_PROXY` for internal hosts reached directly). |
|
|
132
145
|
| `said 401` / `said 403` | The key, or where it's sent, isn't accepted. | Check the key, `key_header`, and any header the gateway wants. The gateway's own message usually names it. |
|
|
133
146
|
| `said 404 … no model called X, which is OpenRouter's name for it` | No `[tiers]`. | Name the gateway's models under `[tiers]`. `doctor` lists the models `/models` offers. |
|
|
147
|
+
| `… is unavailable (503 …), tried 4 times over N s: it is likely …, not your settings` | The gateway, or the model behind it, is down. | Wait, and send it again (↻ under the turn). If it lasts, the gateway's status page or owner. |
|
|
148
|
+
| `… used its whole output budget (N tokens) thinking, and wrote nothing` | A reasoning model ran out of room before it wrote. | A lower tier, or `max_tokens` under `[provider]`. |
|
|
149
|
+
| `! search by meaning: no model …` | Not installed (optional). | `isorite model`. Its failures name each source; offline, `isorite model --from <folder>` with a copy. |
|
|
150
|
+
| `The model could not be fetched:` and a line for npm, GitHub, Hugging Face | None reachable from here. | Copy the model's folder from a machine that has it (`isorite model` there says where), then `isorite model --from <folder>`; or any compatible model. |
|
|
134
151
|
| `said 400: … stream_options` | The gateway refuses `stream_options`. | `stream_usage = false`. |
|
|
135
152
|
| `✗ tools: … answered without calling the tool` | The model or gateway drops tools. | Choose a model that supports tool calls. Iso and the terminal need them. |
|
|
136
153
|
| `isorite term needs OpenCode 2` | OpenCode 1, or none. | `npm i -g @opencode/cli`, or `ISORITE_OPENCODE=…`. |
|
|
@@ -138,5 +155,13 @@ Rules:
|
|
|
138
155
|
| The link doesn't open, over SSH | `localhost` is the remote machine. | `ssh -L 4767:localhost:4767 <host>` from the person's own machine. |
|
|
139
156
|
| The terminal works but uses an unexpected model | Check the line `isorite term` prints: `config.toml`, `ISORITE_GATEWAY_URL`, or OpenCode's own (no key or certificate). | Fix what it names. |
|
|
140
157
|
|
|
158
|
+
## Another model for search by meaning
|
|
159
|
+
|
|
160
|
+
Any ONNX model that gives 384 numbers for a text, with its `tokenizer.json`, can stand in for granite:
|
|
161
|
+
`isorite model --from <folder>`. It's checked by use (loaded, and asked for one vector), copied into the models
|
|
162
|
+
folder, and used from then on; each base's vectors are made again with it. An optional `isorite-model.json` beside
|
|
163
|
+
its files gives `{"name": "…", "pooling": "cls" | "mean"}`: `cls` (the default) takes the first token's state, as
|
|
164
|
+
granite is used; `mean` averages them all, as sentence-transformers models such as all-MiniLM-L6-v2 are used.
|
|
165
|
+
|
|
141
166
|
If the gateway isn't OpenAI-compatible (Bedrock's or Vertex's own APIs, for example), isorite can't use it directly.
|
|
142
167
|
An OpenAI-compatible proxy in front of it, such as LiteLLM, can.
|
package/docs/setup.md
CHANGED
|
@@ -9,7 +9,7 @@ run `isorite doctor`: it checks each step and says what to change.
|
|
|
9
9
|
- **isorite**: `npm i -g isorite`.
|
|
10
10
|
- For the terminal, **OpenCode 2**: `npm i -g @opencode/cli`.
|
|
11
11
|
|
|
12
|
-
`isorite
|
|
12
|
+
`isorite guide` now opens the guide, a knowledge base about isorite with things to do in it. Until a provider is set up, Iso there is the guide's finder: no key, nothing sent anywhere.
|
|
13
13
|
|
|
14
14
|
## 2. A model provider
|
|
15
15
|
|
|
@@ -45,6 +45,7 @@ url = "https://llm-gateway.corp.example/v1"
|
|
|
45
45
|
key = "…"
|
|
46
46
|
key_header = "api-key" # the header the key goes in, as it is, instead of Authorization: Bearer
|
|
47
47
|
stream_usage = false # for a gateway that refuses stream_options; the gauge then counts nothing
|
|
48
|
+
max_tokens = 16000 # the least room a reply is given, for models that think at length
|
|
48
49
|
|
|
49
50
|
# A client certificate (mTLS), for a gateway that asks for one: a .pfx (or .p12) ...
|
|
50
51
|
pfx = "~/certs/me.pfx"
|
|
@@ -77,12 +78,20 @@ If the company's authority isn't installed on the machine, name its certificate
|
|
|
77
78
|
|
|
78
79
|
## 3. Your notes
|
|
79
80
|
|
|
80
|
-
A folder of Markdown files
|
|
81
|
+
A folder of Markdown files. To start from nothing:
|
|
81
82
|
|
|
82
83
|
```sh
|
|
84
|
+
isorite new ~/notes/my-project
|
|
83
85
|
isorite serve ~/notes/my-project
|
|
84
86
|
```
|
|
85
87
|
|
|
88
|
+
`isorite new` makes the folder, empty but for isorite's own `.isorite/`. A folder of notes you have already is
|
|
89
|
+
opened as it is, with `isorite serve`.
|
|
90
|
+
|
|
91
|
+
How you want the base kept (your notation, the level, the language, how notes are named) goes in `AGENTS.md` at
|
|
92
|
+
the top of the notes, which Iso and the terminal's agent are both given. Edit it in Iso's settings, the icon at the
|
|
93
|
+
right of its pane's header, or in any editor. Iso may offer a line for it when you say how you want things done.
|
|
94
|
+
|
|
86
95
|
It prints a link to click: `http://localhost:4767/`. isorite keeps what it needs in the folder's `.isorite/`.
|
|
87
96
|
|
|
88
97
|
## 4. The terminal
|
|
@@ -95,8 +104,25 @@ isorite term ~/notes/my-project
|
|
|
95
104
|
It prints the same link, and which models the terminal uses. OpenCode uses the provider above, through isorite. More
|
|
96
105
|
is in [terminal.md](terminal.md).
|
|
97
106
|
|
|
107
|
+
## Search by meaning (optional)
|
|
108
|
+
|
|
109
|
+
```sh
|
|
110
|
+
isorite model
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
It says which model is installed and where, or fetches it (53 MB, once): from npm, else from a release on GitHub,
|
|
114
|
+
else from Hugging Face. Without it, Iso searches by words and follows links. Where none of those can be reached,
|
|
115
|
+
copy the model's folder from a machine that has it (`isorite model` there says where it is) and run
|
|
116
|
+
`isorite model --from <folder>`. Any compatible model works the same way: an ONNX model that gives 384 numbers,
|
|
117
|
+
with its `tokenizer.json` (for example all-MiniLM-L6-v2, with `{"pooling": "mean"}` in an `isorite-model.json`
|
|
118
|
+
beside it).
|
|
119
|
+
|
|
98
120
|
## When something fails
|
|
99
121
|
|
|
122
|
+
If a gateway is down for a moment (it answers 502, 503 or 504, or drops the connection), Iso and the terminal ask
|
|
123
|
+
it again, up to three times. When it stays down, Iso says it's likely the gateway, not your settings, and the ↻
|
|
124
|
+
under the turn sends it again.
|
|
125
|
+
|
|
100
126
|
```sh
|
|
101
127
|
isorite doctor ~/notes/my-project
|
|
102
128
|
```
|
package/docs/terminal.md
CHANGED
|
@@ -17,7 +17,8 @@ Setting isorite up the first time, or at work behind a company's gateway, is in
|
|
|
17
17
|
|
|
18
18
|
## Your notes for the project
|
|
19
19
|
|
|
20
|
-
Keep the project's notes in a folder of their own, for example `~/notes/<project>`. It can start empty
|
|
20
|
+
Keep the project's notes in a folder of their own, for example `~/notes/<project>`. It can start empty:
|
|
21
|
+
`isorite new ~/notes/<project>` makes it. isorite
|
|
21
22
|
keeps what it needs in the folder's `.isorite/` (the journals of every change, and its settings), made the first
|
|
22
23
|
time the folder is opened. Commit `.isorite/` with the notes, if they're in git.
|
|
23
24
|
|
|
@@ -71,7 +72,12 @@ terminal's session:
|
|
|
71
72
|
- **Notes the agent changes** are written at once, under its name. Turn on provenance in the Note pane's header to
|
|
72
73
|
see the agent's words marked, and the timeline to put a note back.
|
|
73
74
|
|
|
74
|
-
With both off, the pane is Iso's own again.
|
|
75
|
+
With both off, the pane is Iso's own again. The ≡ in its header, or a click on its gauge, opens what Iso is sent:
|
|
76
|
+
a row for each part, with its size. These are the notes you gave and the notes Iso read, the conversation's turns,
|
|
77
|
+
and what is always sent. Iso keeps a note it read in hand, and sets it aside after three turns unused, as a line it
|
|
78
|
+
can read again. A click switches a row on or off. At the head of the conversation, one icon sums the earlier turns
|
|
79
|
+
up into a briefing sent instead of them, and another starts a new conversation from that briefing. Iso sums up on
|
|
80
|
+
its own only when a turn would be more than the model takes.
|
|
75
81
|
|
|
76
82
|
## The models
|
|
77
83
|
|
|
@@ -88,6 +94,9 @@ OpenCode doesn't know a gateway's prices, so Iso's pane shows the session's toke
|
|
|
88
94
|
|
|
89
95
|
## OpenCode's settings
|
|
90
96
|
|
|
97
|
+
OpenCode is given the base's `AGENTS.md` (in its `instructions`), when `isorite term` is named the notes, and the
|
|
98
|
+
knowledge base's ways through `brief_base`: how a note is written and linked, and how the base is kept.
|
|
99
|
+
|
|
91
100
|
isorite keeps OpenCode's settings in `~/.config/isorite/opencode/`, rewritten at each start, so your own OpenCode
|
|
92
101
|
settings are never read or changed. Anything of your own goes in `~/.config/isorite/opencode/local.json`, which is
|
|
93
102
|
never rewritten, and is laid over what isorite writes. For example, to have OpenCode ask before every command (by
|
|
@@ -104,8 +113,11 @@ gateway, or a model served on your own machine). What it fetches, it fetches onc
|
|
|
104
113
|
|
|
105
114
|
- isorite and OpenCode, from npm: through your company's npm mirror, if it has one.
|
|
106
115
|
- The model for search by meaning (optional): `isorite model` gets it from npm too (`isorite-model-granite`), so the
|
|
107
|
-
same mirror serves it; else from Hugging Face. On a machine with
|
|
108
|
-
`isorite model
|
|
116
|
+
same mirror serves it; else from its release on GitHub, else from Hugging Face. On a machine with none of them,
|
|
117
|
+
copy its folder from one that has it (`isorite model` there says where), or download the package from
|
|
118
|
+
`https://github.com/lachlanjs/isorite/releases/tag/model-granite-1.0.0` and unpack it, then
|
|
119
|
+
`isorite model --from <folder>`. Granite's files are checked against their SHA-256; any other compatible model is
|
|
120
|
+
checked by use ([setup-for-agents.md](setup-for-agents.md) says which).
|
|
109
121
|
|
|
110
122
|
## Who can reach it
|
|
111
123
|
|
package/package.json
CHANGED
package/plugin/tui.tsx
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { spawn } from "node:child_process";
|
|
6
6
|
import { BoxRenderable, fg, ImageRenderable, RGBA, StyledText, TextAttributes, TextRenderable, type Renderable, type TextChunk } from "@opentui/core";
|
|
7
7
|
import { Plugin } from "@opencode/plugin/tui";
|
|
8
|
-
import { createEffect, createResource, createSignal, For, onCleanup, Show
|
|
8
|
+
import { createEffect, createResource, createSignal, For, onCleanup, Show } from "solid-js";
|
|
9
9
|
import type { ScrollBoxRenderable } from "@opentui/core";
|
|
10
10
|
import { ago, anchorOf, blocks, describe, momentKey, moments, reach, type Entry, type MapModel } from "../dist/core.js";
|
|
11
11
|
import { drawAtlas, runsOf, type Role } from "./oneatlas.ts";
|
|
@@ -15,7 +15,6 @@ import * as Isorite from "./isorite.ts";
|
|
|
15
15
|
import * as Logo from "./logo.ts";
|
|
16
16
|
import { lapis, quartz, routeColours } from "./palette.ts";
|
|
17
17
|
import { Play, type Step } from "./play.ts";
|
|
18
|
-
import { TOUR, type TourState } from "./tour.ts";
|
|
19
18
|
import { parseSources } from "./sources.ts";
|
|
20
19
|
import * as Wordmark from "./wordmark.ts";
|
|
21
20
|
|
|
@@ -265,9 +264,6 @@ export default Plugin.define({
|
|
|
265
264
|
</text>
|
|
266
265
|
),
|
|
267
266
|
});
|
|
268
|
-
// What the tour (isorite --tutorial) waits on that nothing else counts: the agent's turns ended, Iso's answers,
|
|
269
|
-
// and notes sent from here.
|
|
270
|
-
const [turns, setTurns] = createSignal(0), [isoAnswers, setIsoAnswers] = createSignal(0), [sent, setSent] = createSignal(0);
|
|
271
267
|
|
|
272
268
|
/** [s]: a note, or a passage of it, sent to the agent from the terminal, as from the browser: under "from you". */
|
|
273
269
|
const sendToAgent = async (note: string, passage?: string) => {
|
|
@@ -275,7 +271,7 @@ export default Plugin.define({
|
|
|
275
271
|
const said = await ctx.ui.dialog.prompt({ title: "Send to the agent", description: passage ? "the passage the cursor is on" : `the note “${(titles().get(note) ?? note).toLowerCase()}”`, placeholder: "what you want of it (optional)" });
|
|
276
272
|
if (said === undefined) return;
|
|
277
273
|
await Isorite.post("/api/agent/send", { note, ...(passage ? { passage } : {}), said: said.trim() })
|
|
278
|
-
.
|
|
274
|
+
.catch((e: Error) => ctx.ui.toast?.show?.({ message: e.message, variant: "error" }));
|
|
279
275
|
};
|
|
280
276
|
|
|
281
277
|
const stop = new AbortController();
|
|
@@ -293,8 +289,7 @@ export default Plugin.define({
|
|
|
293
289
|
if (sessionID) void deliver(sessionID);
|
|
294
290
|
return;
|
|
295
291
|
}
|
|
296
|
-
if (type === "agent-done" && typeof data.session === "string") { reading.delete(data.session); hear((n) => n + 1);
|
|
297
|
-
if (type === "iso-done") setIsoAnswers((n) => n + 1);
|
|
292
|
+
if (type === "agent-done" && typeof data.session === "string") { reading.delete(data.session); hear((n) => n + 1); }
|
|
298
293
|
if (type === "note-changed") bumpMap((n) => n + 1);
|
|
299
294
|
if (type !== "agent-step" || data.agent !== Isorite.AGENT || typeof data.session !== "string") return;
|
|
300
295
|
const play = plays.get(data.session) ?? new Play();
|
|
@@ -324,75 +319,6 @@ export default Plugin.define({
|
|
|
324
319
|
return new TextRenderable(ctx.renderer, { content: new StyledText(chunks) });
|
|
325
320
|
});
|
|
326
321
|
|
|
327
|
-
// ------------------------------------------------------------ the tour (isorite --tutorial)
|
|
328
|
-
const TUTORIAL = process.env.ISORITE_TUTORIAL === "1";
|
|
329
|
-
const [stepAt, setStepAt] = createSignal(TUTORIAL ? 0 : TOUR.length);
|
|
330
|
-
const [cardShown, setCardShown] = createSignal(true);
|
|
331
|
-
const tourState = (): TourState => {
|
|
332
|
-
heard();
|
|
333
|
-
const route = ctx.ui.router.current(), shown = note();
|
|
334
|
-
return {
|
|
335
|
-
page: route.type === "plugin" ? (route.name === SPLASH ? "splash" : route.name === NOTE ? "note" : route.name) : route.type,
|
|
336
|
-
turns: turns(),
|
|
337
|
-
inPlay: route.type === "session" ? plays.get(route.sessionID)?.size ?? 0 : 0,
|
|
338
|
-
atlasOpen: ctx.ui.panel.current()?.name === ATLAS,
|
|
339
|
-
note: shownNote(),
|
|
340
|
-
// Not known until the note is read: no step moves on for a note still opening.
|
|
341
|
-
waiting: shown && shown.id === shownNote() ? shown.items.filter((i) => i.type === "change").length : -1,
|
|
342
|
-
past: moment() !== null,
|
|
343
|
-
isoAnswers: isoAnswers(),
|
|
344
|
-
sent: sent(),
|
|
345
|
-
};
|
|
346
|
-
};
|
|
347
|
-
/** Puts the step's prompt in the prompt being typed in, once it has focus and while it is empty. */
|
|
348
|
-
const fillPrompt = (text: string) => {
|
|
349
|
-
let tries = 0;
|
|
350
|
-
const timer = setInterval(() => {
|
|
351
|
-
const editor = ctx.renderer.currentFocusedEditor;
|
|
352
|
-
if (editor && !editor.plainText.trim()) editor.insertText(text);
|
|
353
|
-
if (editor || ++tries > 50) clearInterval(timer);
|
|
354
|
-
}, 100);
|
|
355
|
-
};
|
|
356
|
-
ctx.ui.slot({
|
|
357
|
-
append: "app",
|
|
358
|
-
render() {
|
|
359
|
-
if (!TUTORIAL) return null;
|
|
360
|
-
// A step moves on when what it waits for holds, judged against what held when it began.
|
|
361
|
-
let at = -1, began = untrack(tourState);
|
|
362
|
-
createEffect(() => {
|
|
363
|
-
const i = stepAt(), now = tourState(), step = TOUR[i];
|
|
364
|
-
if (i !== at) { at = i; began = now; if (step?.fill) fillPrompt(step.fill); return; }
|
|
365
|
-
if (step?.until?.(now, began)) setStepAt(i + 1);
|
|
366
|
-
});
|
|
367
|
-
ctx.keymap.layer(() => ({
|
|
368
|
-
mode: "global",
|
|
369
|
-
commands: [
|
|
370
|
-
{ id: "isorite.tour.next", title: "Tour: the next step", palette: true, slash: { name: "next" }, run: () => { setStepAt((i) => Math.min(i + 1, TOUR.length)); } },
|
|
371
|
-
{ id: "isorite.tour.card", title: "Tour: hide or show the card", palette: true, slash: { name: "tour" }, run: () => { setCardShown((v) => !v); } },
|
|
372
|
-
],
|
|
373
|
-
}));
|
|
374
|
-
return (
|
|
375
|
-
<Show when={cardShown() && TOUR[stepAt()]}>
|
|
376
|
-
{(step) => (
|
|
377
|
-
<box position="absolute" right={2} bottom={4} width={48} zIndex={3000} flexDirection="column" paddingLeft={1} paddingRight={1}
|
|
378
|
-
border borderStyle="rounded" borderColor={ground().growth} backgroundColor={ground().g0} title={` tour · ${stepAt() + 1} of ${TOUR.length} `}>
|
|
379
|
-
<text fg={ground().text} wrapMode="word"><b>{step().title}</b></text>
|
|
380
|
-
<text fg={ground().soft} wrapMode="word">{step().text}</text>
|
|
381
|
-
<text> </text>
|
|
382
|
-
<box flexDirection="row">
|
|
383
|
-
<text wrapMode="none">
|
|
384
|
-
<For each={TOUR}>{(_, k) => <span style={{ fg: k() <= stepAt() ? ground().growth : ground().line }}>{k() <= stepAt() ? "■" : "□"}</span>}</For>
|
|
385
|
-
</text>
|
|
386
|
-
<box flexGrow={1} />
|
|
387
|
-
<text fg={ground().faint} wrapMode="none">{step().until ? "/tour hides" : "/next /tour hides"}</text>
|
|
388
|
-
</box>
|
|
389
|
-
</box>
|
|
390
|
-
)}
|
|
391
|
-
</Show>
|
|
392
|
-
);
|
|
393
|
-
},
|
|
394
|
-
});
|
|
395
|
-
|
|
396
322
|
ctx.ui.slot({
|
|
397
323
|
append: "sidebar.content",
|
|
398
324
|
render: (input) => {
|