@nubase/terminal 0.0.0-stage → 0.1.1
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/bin/nubase.js +11 -0
- package/out/main/index.js +3240 -0
- package/out/preload/index.cjs +23 -0
- package/out/renderer/assets/BinaryPreviews-Dvmgctgl.js +262 -0
- package/out/renderer/assets/CodeEditor-BKx61p9c.js +2034 -0
- package/out/renderer/assets/DocumentPreviews-BA1gL-ZJ.js +66 -0
- package/out/renderer/assets/DocxPreview-CiY-SNbh.js +6955 -0
- package/out/renderer/assets/ImagePreview-DpnKfbxj.js +120 -0
- package/out/renderer/assets/PdfPreview-6oeuYaqY.js +24399 -0
- package/out/renderer/assets/PdfPreview-BT-DZbCt.css +6483 -0
- package/out/renderer/assets/SpreadsheetPreviews-B4Qby88A.js +23987 -0
- package/out/renderer/assets/abap-Cel38df4.js +1317 -0
- package/out/renderer/assets/apex-DzOmV_zF.js +355 -0
- package/out/renderer/assets/azcli-Xvapx-7p.js +56 -0
- package/out/renderer/assets/bat-BGMSIlWY.js +126 -0
- package/out/renderer/assets/bicep-CcpV5qxk.js +228 -0
- package/out/renderer/assets/cameligo-BfMDXVEx.js +233 -0
- package/out/renderer/assets/clojure-DFgIAJGu.js +801 -0
- package/out/renderer/assets/codicon-DF1abBS2.ttf +0 -0
- package/out/renderer/assets/coffee-Db4q5aZ8.js +308 -0
- package/out/renderer/assets/cpp-CEvPJv6f.js +450 -0
- package/out/renderer/assets/csharp-CVoBr97X.js +405 -0
- package/out/renderer/assets/csp-Cgzd5gxo.js +49 -0
- package/out/renderer/assets/css-D3M-RRVi.js +287 -0
- package/out/renderer/assets/css.worker-AXSlELAq.js +50031 -0
- package/out/renderer/assets/cssMode-CrMQfSUN.js +101 -0
- package/out/renderer/assets/cursor-editorTextHighlight-kG3uUOwL.svg +8 -0
- package/out/renderer/assets/cypher-DJJgZ_3Q.js +326 -0
- package/out/renderer/assets/dart-Bkg7I-Qe.js +341 -0
- package/out/renderer/assets/dockerfile-B1r36HyK.js +194 -0
- package/out/renderer/assets/ecl-C1Ji6tZG.js +531 -0
- package/out/renderer/assets/editor-CXfYqUZ6.css +4816 -0
- package/out/renderer/assets/editor.api-zF37fz_e.js +127071 -0
- package/out/renderer/assets/elixir-D_-n8lGJ.js +526 -0
- package/out/renderer/assets/fileTypes-nBt-Y6NI.js +56306 -0
- package/out/renderer/assets/flow9-CCdkJPNN.js +179 -0
- package/out/renderer/assets/freemarker2-CBlvIP9I.js +733 -0
- package/out/renderer/assets/fsharp-D5KoOpm3.js +248 -0
- package/out/renderer/assets/go-B7BFdMAL.js +264 -0
- package/out/renderer/assets/graphql-BqIezLde.js +177 -0
- package/out/renderer/assets/handlebars-CG4DAk5_.js +414 -0
- package/out/renderer/assets/hcl-C1GQCKC0.js +222 -0
- package/out/renderer/assets/html-C-_R4s2w.js +335 -0
- package/out/renderer/assets/html.worker-vQ8gO4kF.js +30584 -0
- package/out/renderer/assets/htmlMode-mGTQJ-lr.js +125 -0
- package/out/renderer/assets/index-CQHlaLTi.js +46080 -0
- package/out/renderer/assets/index-DBYJcRdn.css +3124 -0
- package/out/renderer/assets/ini-DDFxEGYN.js +97 -0
- package/out/renderer/assets/java-BqvgSBNj.js +272 -0
- package/out/renderer/assets/javascript-BuIet4p-.js +69 -0
- package/out/renderer/assets/json.worker-DBhrCMTN.js +23259 -0
- package/out/renderer/assets/jsonMode-OAArikPQ.js +769 -0
- package/out/renderer/assets/julia-D30HQT3I.js +688 -0
- package/out/renderer/assets/kotlin-Bql4-FwM.js +303 -0
- package/out/renderer/assets/less-D_rhcFqZ.js +245 -0
- package/out/renderer/assets/lexon-CjhVPeai.js +188 -0
- package/out/renderer/assets/liquid-DJNDWhYP.js +277 -0
- package/out/renderer/assets/lspLanguageFeatures-BIH0_3rO.css +24 -0
- package/out/renderer/assets/lspLanguageFeatures-Bvh4jxNk.js +2583 -0
- package/out/renderer/assets/lua-HQuy0vuU.js +203 -0
- package/out/renderer/assets/m3-CIJ7obE2.js +287 -0
- package/out/renderer/assets/markdown-I0GroHKS.js +265 -0
- package/out/renderer/assets/mdx-B4We-zdi.js +330 -0
- package/out/renderer/assets/mips-DY_Z1uin.js +190 -0
- package/out/renderer/assets/msdax-YkUW9rXl.js +416 -0
- package/out/renderer/assets/mysql-CIm9uV8f.js +921 -0
- package/out/renderer/assets/objective-c-JkhNEfNv.js +233 -0
- package/out/renderer/assets/pascal-Dvq-PxOC.js +289 -0
- package/out/renderer/assets/pascaligo-A9tZTvdV.js +204 -0
- package/out/renderer/assets/pdf_viewer-8SNFor7s.js +7639 -0
- package/out/renderer/assets/perl-C2Ujz2HB.js +720 -0
- package/out/renderer/assets/pgsql-BHB-cYm3.js +879 -0
- package/out/renderer/assets/php-DkNik5Vk.js +548 -0
- package/out/renderer/assets/pla-DQ-RrdDE.js +149 -0
- package/out/renderer/assets/postiats-f1cpw-r_.js +702 -0
- package/out/renderer/assets/powerquery-CwYp-rYn.js +940 -0
- package/out/renderer/assets/powershell-DNVb682q.js +252 -0
- package/out/renderer/assets/protobuf-C73bXEb8.js +663 -0
- package/out/renderer/assets/pug-BxvM69yY.js +419 -0
- package/out/renderer/assets/python-Hlw9kU7X.js +389 -0
- package/out/renderer/assets/qsharp-BCs2AvEL.js +335 -0
- package/out/renderer/assets/r-B7wnzr1V.js +282 -0
- package/out/renderer/assets/razor-BswxUzye.js +615 -0
- package/out/renderer/assets/redis-BQXcxMfX.js +324 -0
- package/out/renderer/assets/redshift-2dZdIUPH.js +838 -0
- package/out/renderer/assets/restructuredtext-CVT2zSWP.js +290 -0
- package/out/renderer/assets/ruby-DjmMI5Dp.js +612 -0
- package/out/renderer/assets/rust-BhSCZj0F.js +382 -0
- package/out/renderer/assets/sb-DaUIZery.js +146 -0
- package/out/renderer/assets/scala-CYIN7D7O.js +589 -0
- package/out/renderer/assets/scheme-DanUmhC2.js +173 -0
- package/out/renderer/assets/scss-BVILLTDt.js +420 -0
- package/out/renderer/assets/shell-Bpairu2a.js +293 -0
- package/out/renderer/assets/solidity-OLm4MSRm.js +1395 -0
- package/out/renderer/assets/sophia-DJz2Vk1M.js +234 -0
- package/out/renderer/assets/sparql-Bk5uRAQt.js +237 -0
- package/out/renderer/assets/sql-DQBN9zz5.js +858 -0
- package/out/renderer/assets/st-50mV4H0S.js +657 -0
- package/out/renderer/assets/swift-Yc0Q28XL.js +376 -0
- package/out/renderer/assets/systemverilog-Du7vdcmu.js +616 -0
- package/out/renderer/assets/tcl-DG7u3TBj.js +312 -0
- package/out/renderer/assets/toggleHighContrast-CKRp-vZ7.css +3691 -0
- package/out/renderer/assets/toggleHighContrast-NyS1pk1Z.js +51247 -0
- package/out/renderer/assets/twig-C49MsNA9.js +438 -0
- package/out/renderer/assets/typescript-F2yeVXIn.js +382 -0
- package/out/renderer/assets/typespec-DA1dz9gQ.js +245 -0
- package/out/renderer/assets/useAsync-Bi56CNnI.js +30 -0
- package/out/renderer/assets/vb-BDNIgfQk.js +447 -0
- package/out/renderer/assets/wgsl-j-0nJ_62.js +457 -0
- package/out/renderer/assets/xml-BfG5_Ah8.js +131 -0
- package/out/renderer/assets/yaml-7gp9FQUG.js +224 -0
- package/out/renderer/index.html +14 -0
- package/package.json +66 -4
- package/resources/skills/nubase-extension-authoring/SKILL.md +273 -0
- package/resources/templates/extension/package.json.template +13 -0
- package/resources/templates/extension/src/host.ts.template +12 -0
- package/resources/templates/extension/src/ui.tsx.template +26 -0
- package/resources/templates/extension/tsconfig.json.template +4 -0
- package/README.md +0 -3
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { c as languages } from "./editor.api-zF37fz_e.js";
|
|
2
|
+
//#region node_modules/monaco-editor/esm/vs/languages/definitions/yaml/yaml.js
|
|
3
|
+
var conf = {
|
|
4
|
+
comments: { lineComment: "#" },
|
|
5
|
+
brackets: [
|
|
6
|
+
["{", "}"],
|
|
7
|
+
["[", "]"],
|
|
8
|
+
["(", ")"]
|
|
9
|
+
],
|
|
10
|
+
autoClosingPairs: [
|
|
11
|
+
{
|
|
12
|
+
open: "{",
|
|
13
|
+
close: "}"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
open: "[",
|
|
17
|
+
close: "]"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
open: "(",
|
|
21
|
+
close: ")"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
open: "\"",
|
|
25
|
+
close: "\""
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
open: "'",
|
|
29
|
+
close: "'"
|
|
30
|
+
}
|
|
31
|
+
],
|
|
32
|
+
surroundingPairs: [
|
|
33
|
+
{
|
|
34
|
+
open: "{",
|
|
35
|
+
close: "}"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
open: "[",
|
|
39
|
+
close: "]"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
open: "(",
|
|
43
|
+
close: ")"
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
open: "\"",
|
|
47
|
+
close: "\""
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
open: "'",
|
|
51
|
+
close: "'"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
folding: { offSide: true },
|
|
55
|
+
onEnterRules: [{
|
|
56
|
+
beforeText: /:\s*$/,
|
|
57
|
+
action: { indentAction: languages.IndentAction.Indent }
|
|
58
|
+
}]
|
|
59
|
+
};
|
|
60
|
+
var language = {
|
|
61
|
+
tokenPostfix: ".yaml",
|
|
62
|
+
brackets: [{
|
|
63
|
+
token: "delimiter.bracket",
|
|
64
|
+
open: "{",
|
|
65
|
+
close: "}"
|
|
66
|
+
}, {
|
|
67
|
+
token: "delimiter.square",
|
|
68
|
+
open: "[",
|
|
69
|
+
close: "]"
|
|
70
|
+
}],
|
|
71
|
+
keywords: [
|
|
72
|
+
"true",
|
|
73
|
+
"True",
|
|
74
|
+
"TRUE",
|
|
75
|
+
"false",
|
|
76
|
+
"False",
|
|
77
|
+
"FALSE",
|
|
78
|
+
"null",
|
|
79
|
+
"Null",
|
|
80
|
+
"Null",
|
|
81
|
+
"~"
|
|
82
|
+
],
|
|
83
|
+
numberInteger: /(?:0|[+-]?[0-9]+)/,
|
|
84
|
+
numberFloat: /(?:0|[+-]?[0-9]+)(?:\.[0-9]+)?(?:e[-+][1-9][0-9]*)?/,
|
|
85
|
+
numberOctal: /0o[0-7]+/,
|
|
86
|
+
numberHex: /0x[0-9a-fA-F]+/,
|
|
87
|
+
numberInfinity: /[+-]?\.(?:inf|Inf|INF)/,
|
|
88
|
+
numberNaN: /\.(?:nan|Nan|NAN)/,
|
|
89
|
+
numberDate: /\d{4}-\d\d-\d\d([Tt ]\d\d:\d\d:\d\d(\.\d+)?(( ?[+-]\d\d?(:\d\d)?)|Z)?)?/,
|
|
90
|
+
escapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/,
|
|
91
|
+
tokenizer: {
|
|
92
|
+
root: [
|
|
93
|
+
{ include: "@whitespace" },
|
|
94
|
+
{ include: "@comment" },
|
|
95
|
+
[/%[^ ]+.*$/, "meta.directive"],
|
|
96
|
+
[/---/, "operators.directivesEnd"],
|
|
97
|
+
[/\.{3}/, "operators.documentEnd"],
|
|
98
|
+
[/[-?:](?= )/, "operators"],
|
|
99
|
+
{ include: "@anchor" },
|
|
100
|
+
{ include: "@tagHandle" },
|
|
101
|
+
{ include: "@flowCollections" },
|
|
102
|
+
{ include: "@blockStyle" },
|
|
103
|
+
[/@numberInteger(?![ \t]*\S+)/, "number"],
|
|
104
|
+
[/@numberFloat(?![ \t]*\S+)/, "number.float"],
|
|
105
|
+
[/@numberOctal(?![ \t]*\S+)/, "number.octal"],
|
|
106
|
+
[/@numberHex(?![ \t]*\S+)/, "number.hex"],
|
|
107
|
+
[/@numberInfinity(?![ \t]*\S+)/, "number.infinity"],
|
|
108
|
+
[/@numberNaN(?![ \t]*\S+)/, "number.nan"],
|
|
109
|
+
[/@numberDate(?![ \t]*\S+)/, "number.date"],
|
|
110
|
+
[/(".*?"|'.*?'|[^#'"]*?)([ \t]*)(:)( |$)/, [
|
|
111
|
+
"type",
|
|
112
|
+
"white",
|
|
113
|
+
"operators",
|
|
114
|
+
"white"
|
|
115
|
+
]],
|
|
116
|
+
{ include: "@flowScalars" },
|
|
117
|
+
[/.+?(?=(\s+#|$))/, { cases: {
|
|
118
|
+
"@keywords": "keyword",
|
|
119
|
+
"@default": "string"
|
|
120
|
+
} }]
|
|
121
|
+
],
|
|
122
|
+
object: [
|
|
123
|
+
{ include: "@whitespace" },
|
|
124
|
+
{ include: "@comment" },
|
|
125
|
+
[
|
|
126
|
+
/\}/,
|
|
127
|
+
"@brackets",
|
|
128
|
+
"@pop"
|
|
129
|
+
],
|
|
130
|
+
[/,/, "delimiter.comma"],
|
|
131
|
+
[/:(?= )/, "operators"],
|
|
132
|
+
[/(?:".*?"|'.*?'|[^,\{\[]+?)(?=: )/, "type"],
|
|
133
|
+
{ include: "@flowCollections" },
|
|
134
|
+
{ include: "@flowScalars" },
|
|
135
|
+
{ include: "@tagHandle" },
|
|
136
|
+
{ include: "@anchor" },
|
|
137
|
+
{ include: "@flowNumber" },
|
|
138
|
+
[/[^\},]+/, { cases: {
|
|
139
|
+
"@keywords": "keyword",
|
|
140
|
+
"@default": "string"
|
|
141
|
+
} }]
|
|
142
|
+
],
|
|
143
|
+
array: [
|
|
144
|
+
{ include: "@whitespace" },
|
|
145
|
+
{ include: "@comment" },
|
|
146
|
+
[
|
|
147
|
+
/\]/,
|
|
148
|
+
"@brackets",
|
|
149
|
+
"@pop"
|
|
150
|
+
],
|
|
151
|
+
[/,/, "delimiter.comma"],
|
|
152
|
+
{ include: "@flowCollections" },
|
|
153
|
+
{ include: "@flowScalars" },
|
|
154
|
+
{ include: "@tagHandle" },
|
|
155
|
+
{ include: "@anchor" },
|
|
156
|
+
{ include: "@flowNumber" },
|
|
157
|
+
[/[^\],]+/, { cases: {
|
|
158
|
+
"@keywords": "keyword",
|
|
159
|
+
"@default": "string"
|
|
160
|
+
} }]
|
|
161
|
+
],
|
|
162
|
+
multiString: [[
|
|
163
|
+
/^( +).+$/,
|
|
164
|
+
"string",
|
|
165
|
+
"@multiStringContinued.$1"
|
|
166
|
+
]],
|
|
167
|
+
multiStringContinued: [[/^( *).+$/, { cases: {
|
|
168
|
+
"$1==$S2": "string",
|
|
169
|
+
"@default": {
|
|
170
|
+
token: "@rematch",
|
|
171
|
+
next: "@popall"
|
|
172
|
+
}
|
|
173
|
+
} }]],
|
|
174
|
+
whitespace: [[/[ \t\r\n]+/, "white"]],
|
|
175
|
+
comment: [[/#.*$/, "comment"]],
|
|
176
|
+
flowCollections: [[
|
|
177
|
+
/\[/,
|
|
178
|
+
"@brackets",
|
|
179
|
+
"@array"
|
|
180
|
+
], [
|
|
181
|
+
/\{/,
|
|
182
|
+
"@brackets",
|
|
183
|
+
"@object"
|
|
184
|
+
]],
|
|
185
|
+
flowScalars: [
|
|
186
|
+
[/"([^"\\]|\\.)*$/, "string.invalid"],
|
|
187
|
+
[/'([^'\\]|\\.)*$/, "string.invalid"],
|
|
188
|
+
[/'[^']*'/, "string"],
|
|
189
|
+
[
|
|
190
|
+
/"/,
|
|
191
|
+
"string",
|
|
192
|
+
"@doubleQuotedString"
|
|
193
|
+
]
|
|
194
|
+
],
|
|
195
|
+
doubleQuotedString: [
|
|
196
|
+
[/[^\\"]+/, "string"],
|
|
197
|
+
[/@escapes/, "string.escape"],
|
|
198
|
+
[/\\./, "string.escape.invalid"],
|
|
199
|
+
[
|
|
200
|
+
/"/,
|
|
201
|
+
"string",
|
|
202
|
+
"@pop"
|
|
203
|
+
]
|
|
204
|
+
],
|
|
205
|
+
blockStyle: [[
|
|
206
|
+
/[>|][0-9]*[+-]?$/,
|
|
207
|
+
"operators",
|
|
208
|
+
"@multiString"
|
|
209
|
+
]],
|
|
210
|
+
flowNumber: [
|
|
211
|
+
[/@numberInteger(?=[ \t]*[,\]\}])/, "number"],
|
|
212
|
+
[/@numberFloat(?=[ \t]*[,\]\}])/, "number.float"],
|
|
213
|
+
[/@numberOctal(?=[ \t]*[,\]\}])/, "number.octal"],
|
|
214
|
+
[/@numberHex(?=[ \t]*[,\]\}])/, "number.hex"],
|
|
215
|
+
[/@numberInfinity(?=[ \t]*[,\]\}])/, "number.infinity"],
|
|
216
|
+
[/@numberNaN(?=[ \t]*[,\]\}])/, "number.nan"],
|
|
217
|
+
[/@numberDate(?=[ \t]*[,\]\}])/, "number.date"]
|
|
218
|
+
],
|
|
219
|
+
tagHandle: [[/\![^ ]*/, "tag"]],
|
|
220
|
+
anchor: [[/[&*][^ ]+/, "namespace"]]
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
//#endregion
|
|
224
|
+
export { conf, language };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en" data-theme="dark">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Nubase</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-CQHlaLTi.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/fileTypes-nBt-Y6NI.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DBYJcRdn.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,68 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nubase/terminal",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Nubase: an AI harness with a real, self-extending UI",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./out/main/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"nubase": "bin/nubase.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"bin",
|
|
13
|
+
"out",
|
|
14
|
+
"resources"
|
|
15
|
+
],
|
|
16
|
+
"publishConfig": {
|
|
17
|
+
"access": "public"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "electron-vite dev --watch",
|
|
21
|
+
"start": "electron-vite preview",
|
|
22
|
+
"build": "electron-vite build",
|
|
23
|
+
"postinstall": "electron-rebuild -f -o node-pty || true",
|
|
24
|
+
"test": "vitest run",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"lint": "biome check .",
|
|
27
|
+
"lint:fix": "biome check . --write --unsafe"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@earendil-works/pi-coding-agent": "1.5.2",
|
|
31
|
+
"@nubase/sdk": "*",
|
|
32
|
+
"@nubase/ui": "*",
|
|
33
|
+
"@tailwindcss/node": "^4.3.5",
|
|
34
|
+
"@tailwindcss/oxide": "^4.3.5",
|
|
35
|
+
"@types/node": "^22.19.0",
|
|
36
|
+
"@types/react": "^19.2.14",
|
|
37
|
+
"@types/react-dom": "^19.2.3",
|
|
38
|
+
"electron": "^45.3.2",
|
|
39
|
+
"esbuild": "^0.27.7",
|
|
40
|
+
"lucide-react": "^0.577.0",
|
|
41
|
+
"node-pty": "^1.1.0",
|
|
42
|
+
"tailwindcss": "^4.3.1",
|
|
43
|
+
"tw-animate-css": "^1.4.0",
|
|
44
|
+
"typescript": "^5.9.3",
|
|
45
|
+
"yauzl": "^3.4.0"
|
|
46
|
+
},
|
|
47
|
+
"devDependencies": {
|
|
48
|
+
"@electron/rebuild": "^4.2.1",
|
|
49
|
+
"@tailwindcss/vite": "^4.3.5",
|
|
50
|
+
"@types/papaparse": "^5.5.2",
|
|
51
|
+
"@types/yauzl": "^3.4.0",
|
|
52
|
+
"@vitejs/plugin-react": "^6.0.3",
|
|
53
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
54
|
+
"@xterm/xterm": "^6.0.0",
|
|
55
|
+
"docx-preview": "^0.4.1",
|
|
56
|
+
"electron-vite": "^6.0.0",
|
|
57
|
+
"monaco-editor": "^0.57.0",
|
|
58
|
+
"papaparse": "^5.7.1",
|
|
59
|
+
"pdfjs-dist": "^6.5.438",
|
|
60
|
+
"react": "^19.2.5",
|
|
61
|
+
"react-dom": "^19.2.5",
|
|
62
|
+
"react-markdown": "^10.1.0",
|
|
63
|
+
"remark-gfm": "^4.0.1",
|
|
64
|
+
"vite": "^8.1.0",
|
|
65
|
+
"vitest": "^4.1.9",
|
|
66
|
+
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz"
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: nubase-extension-authoring
|
|
3
|
+
description: How to build Nubase extensions — widgets, commands, keybindings, themes and agent tools — that hot-load into the running Nubase UI. Read before creating or editing anything in the Nubase extensions folder.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Writing Nubase extensions
|
|
7
|
+
|
|
8
|
+
Nubase's UI is a dock of blocks. Each block shows a **widget**. Widgets,
|
|
9
|
+
commands, keybindings, themes and agent tools come from **extensions**:
|
|
10
|
+
small TypeScript projects in the Nubase extensions folder
|
|
11
|
+
(`~/.nubase/extensions/<id>/`). Built-in widgets use exactly this API.
|
|
12
|
+
|
|
13
|
+
Each top-level tab is a folder. `~/.nubase/extensions` is **global**: those
|
|
14
|
+
extensions load in every folder. A folder can also have its own
|
|
15
|
+
`<folder>/.nubase/extensions/<id>/`, which loads only in that folder's tab
|
|
16
|
+
and replaces a global extension with the same id. Create global extensions
|
|
17
|
+
unless the user asks for one specific to the current folder or project
|
|
18
|
+
(`nubase_create_extension` with `scope: "folder"`). Both kinds have the same
|
|
19
|
+
layout and API.
|
|
20
|
+
|
|
21
|
+
## Workflow
|
|
22
|
+
|
|
23
|
+
1. `nubase_create_extension` with a kebab-case `id` (set `withHost: true` if
|
|
24
|
+
you need secrets, HTTP without CORS, Node APIs, or agent tools).
|
|
25
|
+
2. Edit `src/ui.tsx` (and `src/host.ts`). Never edit `dist/` or
|
|
26
|
+
`../tsconfig.base.json`.
|
|
27
|
+
3. `nubase_build_extension` — fix **every** error it reports (esbuild,
|
|
28
|
+
TypeScript, Tailwind) and build again until it succeeds. The previous
|
|
29
|
+
working version stays loaded while a build is broken.
|
|
30
|
+
4. `nubase_open_widget` with the widget id, then `nubase_screenshot` with the
|
|
31
|
+
returned block id to check how it looks. Iterate on visuals.
|
|
32
|
+
5. Runtime errors: `nubase_extension_logs`. Bad change? `nubase_revert_extension`.
|
|
33
|
+
|
|
34
|
+
Successful builds are committed to git automatically.
|
|
35
|
+
|
|
36
|
+
## Layout of an extension
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<id>/
|
|
40
|
+
package.json { "nubase": { "id", "displayName", "description", "ui": "src/ui.tsx", "host": "src/host.ts" } }
|
|
41
|
+
tsconfig.json { "extends": "../tsconfig.base.json", "include": ["src"] }
|
|
42
|
+
src/ui.tsx renderer half (React) — optional
|
|
43
|
+
src/host.ts Node half (Electron main) — optional
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
`nubase.id` must equal the folder name. Widget and command ids should be
|
|
47
|
+
namespaced with the extension id: `jira.myTickets`, `jira.refresh`.
|
|
48
|
+
|
|
49
|
+
## Available imports
|
|
50
|
+
|
|
51
|
+
Provided by Nubase at runtime (do **not** npm install these):
|
|
52
|
+
|
|
53
|
+
- `react`, `react-dom`, `react/jsx-runtime`
|
|
54
|
+
- `@nubase/sdk` (ui half) and `@nubase/sdk/host` (host half)
|
|
55
|
+
- `@nubase/ui` — components and utilities (below)
|
|
56
|
+
- `lucide-react` — icons
|
|
57
|
+
|
|
58
|
+
Any other npm package: run `npm install <pkg>` inside the extension folder;
|
|
59
|
+
it is bundled at build time. Prefer `fetch` over HTTP client libraries.
|
|
60
|
+
|
|
61
|
+
## UI half (`src/ui.tsx`)
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { defineUiExtension, type WidgetProps } from "@nubase/sdk";
|
|
65
|
+
import { Button, Card, CardContent, CardHeader, CardTitle } from "@nubase/ui";
|
|
66
|
+
import { Ticket } from "lucide-react";
|
|
67
|
+
|
|
68
|
+
type State = { filter: string };
|
|
69
|
+
|
|
70
|
+
const TicketsWidget = ({ ctx, state, setState, setTitle, isFocused }: WidgetProps<State>) => {
|
|
71
|
+
// state: persisted per block (lives in the layout; survives reloads/restarts)
|
|
72
|
+
// setState(next): persist new state — keep it small & JSON-serializable
|
|
73
|
+
// setTitle("…"): block header title; ctx: the UiContext (see below)
|
|
74
|
+
return <div className="h-full overflow-auto p-3">…</div>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export default defineUiExtension({
|
|
78
|
+
activate(ctx) {
|
|
79
|
+
ctx.widgets.register<State>({
|
|
80
|
+
id: "jira.myTickets",
|
|
81
|
+
title: "my tickets", // short, lowercase reads best in the launcher
|
|
82
|
+
description: "Jira tickets assigned to me",
|
|
83
|
+
icon: Ticket,
|
|
84
|
+
defaultState: { filter: "" },
|
|
85
|
+
singleton: false, // true → only one instance, reopening focuses it
|
|
86
|
+
component: TicketsWidget,
|
|
87
|
+
});
|
|
88
|
+
ctx.commands.register({
|
|
89
|
+
id: "jira.refresh",
|
|
90
|
+
title: "Refresh Tickets",
|
|
91
|
+
category: "Jira",
|
|
92
|
+
execute: async () => { /* … */ },
|
|
93
|
+
});
|
|
94
|
+
ctx.keybindings.register({ key: "meta+shift+j", command: "jira.refresh" });
|
|
95
|
+
// Optional: return extra Disposables (timers, listeners) to clean up on reload.
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Everything registered through `ctx` is removed automatically when the
|
|
101
|
+
extension reloads. Widgets are remounted on every rebuild — keep anything
|
|
102
|
+
that must survive in `state`, `ctx.storage` or the host half.
|
|
103
|
+
|
|
104
|
+
### UiContext (`ctx`)
|
|
105
|
+
|
|
106
|
+
| API | Purpose |
|
|
107
|
+
| --- | --- |
|
|
108
|
+
| `ctx.widgets.register(def)` | Add a widget type (shows in the launcher) |
|
|
109
|
+
| `ctx.commands.register({id,title,category?,execute})` / `.execute(id,args)` | Commands appear in the ⌘K palette; the agent can run them too |
|
|
110
|
+
| `ctx.keybindings.register({key, command, args?, when?})` | `key` like `meta+k`, chords `"ctrl+k ctrl+c"`; `when` e.g. `focusedWidget == jira.myTickets` |
|
|
111
|
+
| `ctx.themes.register(theme)` / `.setActive(id)` / `.getAll()` / `.active()` | Contribute or switch color themes |
|
|
112
|
+
| `ctx.layout.openWidget(id, {placement, state})` / `.closeBlock` / `.focusBlock` | Arrange the dock (`placement`: right, below, left, above, replace) |
|
|
113
|
+
| `ctx.host.call<T>(method, ...args)` / `ctx.host.on(event, fn)` | Talk to **this extension's** host half |
|
|
114
|
+
| `ctx.workspace` | The folder this tab is open on: `{ id, folder, title, isHome }`. Default to `ctx.workspace.folder` for anything path-related |
|
|
115
|
+
| `ctx.fs` | `readText`, `writeText`, `stat`, `readDirectory`, `mkdir`, `delete`, `rename`, `watch`, `homeDir` (absolute paths); `resourceUrl(path)` — a URL for `<img>`/`<video>`/`<iframe>`/`fetch` (Range supported) that streams the file, for binary or large files; `thumbnail(path, size?)` — OS thumbnail data URL (macOS/Windows); `describe(path)` — a `PreviewFile` |
|
|
116
|
+
| `ctx.previewers.register(def)` | Add a file viewer to the files widget (see below) |
|
|
117
|
+
| `ctx.shell` | `openPath(path)` (default app), `showItemInFolder(path)`, `quickLook(path)` (macOS) |
|
|
118
|
+
| `ctx.agent` | `prompt(text, images?)` (images: `{ data: base64, mimeType }[]`), `onEvent`, `getMessages`, `getState`, … — drive the AI agent |
|
|
119
|
+
| `ctx.storage.get/set(key, value)` | Small persisted JSON per extension |
|
|
120
|
+
| `ctx.dialogs.confirm/input/select` | Native-looking modal dialogs (promise-based) |
|
|
121
|
+
| `ctx.notify(message, "info" \| "error")` | Toast |
|
|
122
|
+
| `ctx.openExternal(url)` | Open in the user's browser |
|
|
123
|
+
|
|
124
|
+
### File previewers
|
|
125
|
+
|
|
126
|
+
The files widget shows each file with the highest-priority previewer that
|
|
127
|
+
accepts it; the others appear in its "view as" menu. Add one for a format
|
|
128
|
+
the user cares about (parquet, sqlite, GeoJSON, …) instead of a new widget:
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
import { definePreviewer, type PreviewerProps } from "@nubase/sdk";
|
|
132
|
+
import { lazy } from "react";
|
|
133
|
+
|
|
134
|
+
const GeoJsonMap = ({ file, ctx }: PreviewerProps) => {
|
|
135
|
+
// file: { path, name, extension, size, mtimeMs, url, isBinary }
|
|
136
|
+
// file.url streams the bytes and changes when the file changes: depend on it in effects.
|
|
137
|
+
// fetch(file.url).then((r) => r.json()) …, or <img src={file.url} />
|
|
138
|
+
return <div className="h-full overflow-auto p-3">…</div>;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
// in activate(ctx):
|
|
142
|
+
ctx.previewers.register(
|
|
143
|
+
definePreviewer({
|
|
144
|
+
id: "geo.map",
|
|
145
|
+
title: "Map", // label in the "view as" menu
|
|
146
|
+
extensions: ["geojson"], // lowercase, no dot
|
|
147
|
+
match: (file) => file.size < 50e6, // optional extra test
|
|
148
|
+
priority: 100, // built-ins: viewers 100, text editor 10, hex -50, info -100
|
|
149
|
+
component: GeoJsonMap, // React.lazy(() => import("./heavy")) for big libraries
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Registering an id a built-in already uses (e.g. `nubase.preview.pdf`)
|
|
155
|
+
replaces that viewer. To show a file to the user, run the
|
|
156
|
+
`files.preview` command: `ctx.commands.execute("files.preview", { path, placement: "right" })`.
|
|
157
|
+
|
|
158
|
+
## Host half (`src/host.ts`)
|
|
159
|
+
|
|
160
|
+
Runs in Node inside Nubase. Use it for secrets, network calls (no CORS),
|
|
161
|
+
the file system, child processes, and agent tools.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { defineHostExtension } from "@nubase/sdk/host";
|
|
165
|
+
|
|
166
|
+
export default defineHostExtension({
|
|
167
|
+
activate(ctx) {
|
|
168
|
+
const token = async () =>
|
|
169
|
+
(await ctx.secrets.get("token")) ??
|
|
170
|
+
(await ctx.secrets.prompt("token", "Jira API token (id.atlassian.com → Security)"));
|
|
171
|
+
|
|
172
|
+
const search = async (jql: string) => {
|
|
173
|
+
const res = await fetch(`https://example.atlassian.net/rest/api/3/search?jql=${encodeURIComponent(jql)}`, {
|
|
174
|
+
headers: { Authorization: `Bearer ${await token()}`, Accept: "application/json" },
|
|
175
|
+
});
|
|
176
|
+
if (!res.ok) throw new Error(`Jira ${res.status}: ${await res.text()}`);
|
|
177
|
+
return (await res.json()).issues as { key: string; fields: { summary: string } }[];
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
return [
|
|
181
|
+
ctx.rpc.handle("myTickets", () => search("assignee = currentUser() AND resolution = Unresolved")),
|
|
182
|
+
ctx.agent.registerTool({
|
|
183
|
+
name: "jira_search", // snake_case, globally unique
|
|
184
|
+
description: "Search Jira issues with JQL. Returns key and summary.",
|
|
185
|
+
parameters: { // JSON Schema
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: { jql: { type: "string" } },
|
|
188
|
+
required: ["jql"],
|
|
189
|
+
},
|
|
190
|
+
execute: async ({ jql }: { jql: string }) =>
|
|
191
|
+
(await search(jql)).map((i) => `${i.key}: ${i.fields.summary}`).join("\n") || "No issues.",
|
|
192
|
+
}),
|
|
193
|
+
];
|
|
194
|
+
},
|
|
195
|
+
});
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
| API | Purpose |
|
|
199
|
+
| --- | --- |
|
|
200
|
+
| `ctx.rpc.handle(method, fn)` | Expose a method to this extension's widgets (`ctx.host.call`) |
|
|
201
|
+
| `ctx.rpc.emit(event, payload)` | Push events to widgets (`ctx.host.on`) |
|
|
202
|
+
| `ctx.secrets.get/set/delete(key)`, `ctx.secrets.prompt(key, title)` | Encrypted secrets; `prompt` asks the user in a masked dialog. **Never** ask for secrets in chat or put them in code |
|
|
203
|
+
| `ctx.storage.get/set` | Same store as the ui half's `ctx.storage` |
|
|
204
|
+
| `ctx.agent.registerTool({...})` | Give the AI agent a new tool (available from its next turn) |
|
|
205
|
+
| `ctx.workspace` | The folder this activation belongs to. A host half is activated **once per open folder tab**, so keep per-folder state inside `activate` |
|
|
206
|
+
| `ctx.fs`, `ctx.log.info/warn/error` | File system; logs visible via `nubase_extension_logs` |
|
|
207
|
+
|
|
208
|
+
Calling the host from a widget, with loading and error states:
|
|
209
|
+
|
|
210
|
+
```tsx
|
|
211
|
+
const [issues, setIssues] = useState<Issue[]>();
|
|
212
|
+
const [error, setError] = useState<string>();
|
|
213
|
+
useEffect(() => {
|
|
214
|
+
ctx.host.call<Issue[]>("myTickets").then(setIssues, (e) => setError(String(e)));
|
|
215
|
+
}, [ctx]);
|
|
216
|
+
if (error) return <Callout variant="danger" className="m-3">{error}</Callout>;
|
|
217
|
+
if (!issues) return <div className="flex h-full items-center justify-center"><ActivityIndicator /></div>;
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Look and feel
|
|
221
|
+
|
|
222
|
+
Widgets must look native. Use Tailwind classes with **theme tokens only** —
|
|
223
|
+
never hard-coded colors — so every theme works:
|
|
224
|
+
|
|
225
|
+
- Surfaces: `bg-background`, `bg-card`, `bg-muted`, `bg-secondary`, `bg-accent`
|
|
226
|
+
- Text: `text-foreground`, `text-muted-foreground`, `text-primary`, `text-destructive`
|
|
227
|
+
- Borders: `border` (uses `border-border`), `border-input`, `ring-ring`
|
|
228
|
+
- Accents for data: `text-chart-1` … `text-chart-5`, `bg-chart-2/15` (opacity modifiers work)
|
|
229
|
+
- Radius: `rounded-md`, `rounded-lg`; type scale `text-xs`/`text-sm` for dense UI
|
|
230
|
+
|
|
231
|
+
The widget fills its block: make the root `h-full` and let content scroll
|
|
232
|
+
with `overflow-auto`. Use `text-sm` by default, `p-3` padding, `gap-2`.
|
|
233
|
+
|
|
234
|
+
Nubase is compact like an editor, and its density comes from theme tokens:
|
|
235
|
+
the root font size, Tailwind's spacing unit and `--radius`. Size things with
|
|
236
|
+
the Tailwind scale (`text-xs`, `p-2`, `h-8`, `rounded-md`) so they follow it.
|
|
237
|
+
Never use pixel font sizes (`text-[13px]`, `style={{ fontSize: 13 }}`). For
|
|
238
|
+
renderers that need pixels (canvas, Monaco, xterm), read the tokens with
|
|
239
|
+
`readCssPixels("--code-font-size", 12)` from `@nubase/ui` (also
|
|
240
|
+
`--code-line-height`).
|
|
241
|
+
Mark copyable text with the `selectable` class (the app disables text
|
|
242
|
+
selection by default).
|
|
243
|
+
|
|
244
|
+
`@nubase/ui` components:
|
|
245
|
+
|
|
246
|
+
- `Button` (`variant`: default, secondary, outline, ghost, destructive; `size`: default, sm, icon, icon-sm; `isLoading`)
|
|
247
|
+
- `ActivityIndicator` (`size`: xs…xl) — use for every loading state, never a custom spinner
|
|
248
|
+
- `Card`, `CardHeader`, `CardTitle`, `CardDescription`, `CardContent`, `CardFooter`, `CardAction`
|
|
249
|
+
- `Callout` (`variant`: info, danger)
|
|
250
|
+
- `TextInput`, `SearchTextInput`, `Select` (`options: {value,label}[]`, `value`, `onChange`), `Checkbox`, `Switch`, `Label`, `FormControl` (`label`, `hint`, `error`)
|
|
251
|
+
- `InputGroup` + `InputGroupInput` / `InputGroupTextarea` + `InputGroupAddon` — a field with icons or buttons inside (search box, chat composer). Add `className="h-auto"` to the group around a textarea so it grows
|
|
252
|
+
- `Tabs`, `TabsList`, `TabsTrigger`, `TabsContent`
|
|
253
|
+
- `DropdownMenu`, `DropdownMenuTrigger`, `DropdownMenuContent`, `DropdownMenuItem`, `DropdownMenuSeparator`, …
|
|
254
|
+
- `Toggle`, `ButtonBar`, `cn(...classes)` for conditional classes
|
|
255
|
+
- `showToast(message, "default" | "error")`
|
|
256
|
+
|
|
257
|
+
## Rules
|
|
258
|
+
|
|
259
|
+
- **Never write a raw `<input>`, `<textarea>` or `<select>`.** Use the
|
|
260
|
+
`@nubase/ui` field components above. They all share one definition of the
|
|
261
|
+
field surface and focus style, so a hand-styled field always looks out of
|
|
262
|
+
place. Built-in widgets are linted for this. Change size with `className`
|
|
263
|
+
(e.g. `h-6 text-xs` in a toolbar), never the border, background or focus
|
|
264
|
+
styles.
|
|
265
|
+
|
|
266
|
+
- Export the extension with `export default defineUiExtension(...)` /
|
|
267
|
+
`export default defineHostExtension(...)`.
|
|
268
|
+
- Keep widget state JSON-serializable and small; cache bulky data in the host
|
|
269
|
+
half or `ctx.storage`.
|
|
270
|
+
- Clean up intervals and subscriptions in `useEffect` cleanups.
|
|
271
|
+
- Handle errors visibly in the widget (Callout) — don't fail silently.
|
|
272
|
+
- Polling: at most every 30s unless the user asks otherwise.
|
|
273
|
+
- After building, always screenshot the widget and fix visual problems.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "nubase-extension-__ID__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"nubase": {
|
|
7
|
+
"id": "__ID__",
|
|
8
|
+
"displayName": "__DISPLAY_NAME__",
|
|
9
|
+
"description": "__DESCRIPTION__",
|
|
10
|
+
"ui": "src/ui.tsx",
|
|
11
|
+
"host": "src/host.ts"
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineHostExtension } from "@nubase/sdk/host";
|
|
2
|
+
|
|
3
|
+
// Runs in Node (Electron main process). Use it for secrets, HTTP calls
|
|
4
|
+
// without CORS, the file system, and tools for the agent.
|
|
5
|
+
export default defineHostExtension({
|
|
6
|
+
activate(ctx) {
|
|
7
|
+
return [
|
|
8
|
+
// Widgets call this with: ctx.host.call<string>("hello", "world")
|
|
9
|
+
ctx.rpc.handle("hello", (name: string) => `Hello, ${name}!`),
|
|
10
|
+
];
|
|
11
|
+
},
|
|
12
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineUiExtension, type WidgetProps } from "@nubase/sdk";
|
|
2
|
+
import { Button } from "@nubase/ui";
|
|
3
|
+
import { Sparkles } from "lucide-react";
|
|
4
|
+
|
|
5
|
+
type State = { count: number };
|
|
6
|
+
|
|
7
|
+
const MainWidget = ({ state, setState }: WidgetProps<State>) => (
|
|
8
|
+
<div className="flex h-full flex-col items-center justify-center gap-3 p-4">
|
|
9
|
+
<p className="text-muted-foreground text-sm">__DISPLAY_NAME__</p>
|
|
10
|
+
<p className="font-semibold text-3xl">{state.count}</p>
|
|
11
|
+
<Button onClick={() => setState({ count: state.count + 1 })}>Increment</Button>
|
|
12
|
+
</div>
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
export default defineUiExtension({
|
|
16
|
+
activate(ctx) {
|
|
17
|
+
ctx.widgets.register<State>({
|
|
18
|
+
id: "__WIDGET_ID__",
|
|
19
|
+
title: "__DISPLAY_NAME__",
|
|
20
|
+
description: "__DESCRIPTION__",
|
|
21
|
+
icon: Sparkles,
|
|
22
|
+
defaultState: { count: 0 },
|
|
23
|
+
component: MainWidget,
|
|
24
|
+
});
|
|
25
|
+
},
|
|
26
|
+
});
|
package/README.md
DELETED