hakoniwa-term 0.0.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/README.md +10 -0
- package/dist/index.cjs +1 -0
- package/dist/index.css +2 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +149 -0
- package/package.json +45 -0
package/README.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# hakoniwa-term (箱庭-term)
|
|
2
|
+
|
|
3
|
+
A programmable, retro hacker-style terminal UI component for React applications with complete style isolation.
|
|
4
|
+
|
|
5
|
+
## what is hakoniwa(箱庭)?
|
|
6
|
+
>In Japanese, 「箱庭(はこにわ / hakoniwa)」literally means “box garden.”
|
|
7
|
+
Originally it refers to a small, self‑contained miniature garden or landscape arranged inside a box or tray—like tiny rocks, plants, and buildings forming a little world you can look into from the outside.
|
|
8
|
+
|
|
9
|
+
## screenshot
|
|
10
|
+

|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,a)=>(a=n==null?{}:e(i(n)),o(r||!n||!n.__esModule?t(a,`default`,{value:n,enumerable:!0}):a,n));let c=require("react");c=s(c);let l=require("lucide-react"),u=require("react/jsx-runtime");var d={terminalRoot:`_terminalRoot_16n8k_1`,titleBar:`_titleBar_16n8k_33`,titleLeft:`_titleLeft_16n8k_53`,titleText:`_titleText_16n8k_65`,titleRight:`_titleRight_16n8k_79`,closeButton:`_closeButton_16n8k_91`,contentBox:`_contentBox_16n8k_123`,logRow:`_logRow_16n8k_151`,logInput:`_logInput_16n8k_163`,logError:`_logError_16n8k_165`,logSuccess:`_logSuccess_16n8k_167`,logOutput:`_logOutput_16n8k_169`,progressBarContainer:`_progressBarContainer_16n8k_173`,progressHeader:`_progressHeader_16n8k_181`,progressTrack:`_progressTrack_16n8k_199`,progressBar:`_progressBar_16n8k_173`,inputForm:`_inputForm_16n8k_229`,prompt:`_prompt_16n8k_247`,inputField:`_inputField_16n8k_257`,submitButton:`_submitButton_16n8k_287`};function f({promptString:e=`user@terminal:~$`,placeholder:t=`Type a command...`,systemLockedText:n=`System locked during execution...`,commandNotFoundFormatter:r=e=>`Command not found: "${e}".`,initialHistory:i=[],commands:a,title:o=`terminal`,showCloseButton:s=!0,onClose:f,headerRightActions:p}){let[m,h]=(0,c.useState)(``),[g,_]=(0,c.useState)(i),[v,y]=(0,c.useState)(!1),[b,x]=(0,c.useState)(null),[S,C]=(0,c.useState)(``),w=(0,c.useRef)(null),T=(0,c.useRef)(null);(0,c.useEffect)(()=>{w.current&&(w.current.scrollTop=w.current.scrollHeight)},[g,b,S,v]);let E=()=>{T.current&&T.current.focus()},D=async t=>{t.preventDefault();let n=m.trim();if(!n||v)return;_(t=>[...t,{type:`input`,text:`${e} ${n}`}]),h(``);let i=n.split(` `),o=i[0].toLowerCase();if(o===`clear`){_([]);return}if(a&&a[o]){y(!0);try{let e=a[o](i);for await(let t of e)t.type===`log`?_(e=>[...e,t.log]):t.type===`progress`&&(x(t.percent),t.text&&C(t.text))}catch(e){_(t=>[...t,{type:`error`,text:`Execution error: ${e instanceof Error?e.message:String(e)}`}])}finally{y(!1),x(null),C(``)}}else _(e=>[...e,{type:`error`,text:r(o)}])},O=e=>e===`input`?d.logInput:e===`error`?d.logError:e===`success`?d.logSuccess:d.logOutput;return(0,u.jsxs)(`div`,{className:d.terminalRoot,onClick:E,children:[(0,u.jsxs)(`div`,{className:d.titleBar,children:[(0,u.jsxs)(`div`,{className:d.titleLeft,children:[(0,u.jsx)(l.Terminal,{className:`w-4 h-4`,style:{color:`#10b981`}}),(0,u.jsx)(`span`,{className:d.titleText,children:o})]}),(0,u.jsxs)(`div`,{className:d.titleRight,children:[p,s&&f&&(0,u.jsx)(`button`,{onClick:e=>{e.stopPropagation(),f()},className:d.closeButton,children:(0,u.jsx)(l.X,{className:`w-4 h-4`})})]})]}),(0,u.jsxs)(`div`,{ref:w,className:d.contentBox,children:[g.map((e,t)=>(0,u.jsx)(`div`,{className:`${d.logRow} ${O(e.type)}`,children:e.text},t)),b!==null&&(0,u.jsxs)(`div`,{className:d.progressBarContainer,children:[(0,u.jsxs)(`div`,{className:d.progressHeader,children:[(0,u.jsx)(`span`,{children:S}),(0,u.jsxs)(`span`,{children:[b,`%`]})]}),(0,u.jsx)(`div`,{className:d.progressTrack,children:(0,u.jsx)(`div`,{className:d.progressBar,style:{width:`${b}%`}})})]}),(0,u.jsxs)(`form`,{onSubmit:D,className:d.inputForm,children:[(0,u.jsx)(`span`,{className:d.prompt,children:e}),(0,u.jsx)(`input`,{ref:T,type:`text`,value:m,onChange:e=>h(e.target.value),disabled:v,placeholder:v?n:t,className:d.inputField,autoComplete:`off`,autoCorrect:`off`,autoCapitalize:`off`,spellCheck:`false`}),(0,u.jsx)(`button`,{type:`submit`,className:d.submitButton,children:(0,u.jsx)(l.CornerDownLeft,{className:`w-4 h-4`})})]})]})]})}exports.Terminal=f;
|
package/dist/index.css
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
._terminalRoot_16n8k_1{color:#34d399e6;z-index:10;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background-color:#090a0ff2;border:1px solid #10b9814d;border-radius:.5rem;width:100%;max-width:56rem;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:.875rem;position:relative;overflow:hidden;box-shadow:0 25px 50px -12px #00000040}._titleBar_16n8k_33{-webkit-user-select:none;user-select:none;background-color:#0e1017;border-bottom:1px solid #10b98133;justify-content:space-between;align-items:center;padding:.625rem 1rem;display:flex}._titleLeft_16n8k_53{align-items:center;gap:.5rem;display:flex}._titleText_16n8k_65{letter-spacing:.05em;color:#9ca3af;font-size:.75rem;font-weight:600}._titleRight_16n8k_79{align-items:center;gap:.5rem;display:flex}._closeButton_16n8k_91{cursor:pointer;color:#ef4444;background:0 0;border:none;border-radius:.25rem;align-items:center;padding:.125rem;transition:background-color .2s;display:flex}._closeButton_16n8k_91:hover{background-color:#ef444433}._contentBox_16n8k_123{height:24rem;padding:1.25rem;overflow-y:auto}._contentBox_16n8k_123::-webkit-scrollbar{width:6px}._contentBox_16n8k_123::-webkit-scrollbar-thumb{background-color:#10b9811a;border-radius:3px}._logRow_16n8k_151{white-space:pre-wrap;margin-bottom:.625rem;line-height:1.625}._logInput_16n8k_163{color:#f3f4f6;font-weight:600}._logError_16n8k_165{color:#f87171}._logSuccess_16n8k_167{color:#6ee7b7;font-weight:700}._logOutput_16n8k_169{color:#34d399cc}._progressBarContainer_16n8k_173{padding:.5rem 0}._progressHeader_16n8k_181{color:#9ca3af;justify-content:space-between;align-items:center;margin-bottom:.25rem;font-size:.75rem;display:flex}._progressTrack_16n8k_199{background-color:#064e3b66;border:1px solid #10b98133;border-radius:.25rem;width:100%;height:.5rem;overflow:hidden}._progressBar_16n8k_173{background-color:#10b981;height:100%;transition:width .3s}._inputForm_16n8k_229{border-top:1px solid #10b9811a;align-items:center;gap:.5rem;margin-top:.5rem;padding-top:.5rem;display:flex}._prompt_16n8k_247{color:#10b981;font-weight:700}._inputField_16n8k_257{color:#f3f4f6;background:0 0;border:none;outline:none;flex:1;padding:0;font-family:inherit;font-size:.875rem}._inputField_16n8k_257::placeholder{color:#9ca3af80}._submitButton_16n8k_287{color:#10b98180;cursor:pointer;background:0 0;border:none;align-items:center;transition:color .2s;display:flex}._submitButton_16n8k_287:hover{color:#34d399}
|
|
2
|
+
/*$vite$:1*/
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { default as default_2 } from 'react';
|
|
2
|
+
|
|
3
|
+
export declare type CommandAction = (args: string[]) => AsyncGenerator<YieldChunk, void, unknown>;
|
|
4
|
+
|
|
5
|
+
export declare interface CommandLog {
|
|
6
|
+
type: 'input' | 'output' | 'error' | 'success';
|
|
7
|
+
text: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export declare function Terminal({ promptString, placeholder, systemLockedText, commandNotFoundFormatter, initialHistory, commands, title, showCloseButton, onClose, headerRightActions, }: TerminalProps): default_2.JSX.Element;
|
|
11
|
+
|
|
12
|
+
export declare interface TerminalProps {
|
|
13
|
+
promptString?: string;
|
|
14
|
+
placeholder?: string;
|
|
15
|
+
systemLockedText?: string;
|
|
16
|
+
commandNotFoundFormatter?: (cmd: string) => string;
|
|
17
|
+
initialHistory?: CommandLog[];
|
|
18
|
+
commands: Record<string, CommandAction>;
|
|
19
|
+
title?: default_2.ReactNode;
|
|
20
|
+
showCloseButton?: boolean;
|
|
21
|
+
onClose?: () => void;
|
|
22
|
+
headerRightActions?: default_2.ReactNode;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export declare type YieldChunk = {
|
|
26
|
+
type: 'log';
|
|
27
|
+
log: CommandLog;
|
|
28
|
+
} | {
|
|
29
|
+
type: 'progress';
|
|
30
|
+
percent: number;
|
|
31
|
+
text?: string;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
export { }
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { useEffect as e, useRef as t, useState as n } from "react";
|
|
2
|
+
import { CornerDownLeft as r, Terminal as i, X as a } from "lucide-react";
|
|
3
|
+
import { jsx as o, jsxs as s } from "react/jsx-runtime";
|
|
4
|
+
var c = {
|
|
5
|
+
terminalRoot: "_terminalRoot_16n8k_1",
|
|
6
|
+
titleBar: "_titleBar_16n8k_33",
|
|
7
|
+
titleLeft: "_titleLeft_16n8k_53",
|
|
8
|
+
titleText: "_titleText_16n8k_65",
|
|
9
|
+
titleRight: "_titleRight_16n8k_79",
|
|
10
|
+
closeButton: "_closeButton_16n8k_91",
|
|
11
|
+
contentBox: "_contentBox_16n8k_123",
|
|
12
|
+
logRow: "_logRow_16n8k_151",
|
|
13
|
+
logInput: "_logInput_16n8k_163",
|
|
14
|
+
logError: "_logError_16n8k_165",
|
|
15
|
+
logSuccess: "_logSuccess_16n8k_167",
|
|
16
|
+
logOutput: "_logOutput_16n8k_169",
|
|
17
|
+
progressBarContainer: "_progressBarContainer_16n8k_173",
|
|
18
|
+
progressHeader: "_progressHeader_16n8k_181",
|
|
19
|
+
progressTrack: "_progressTrack_16n8k_199",
|
|
20
|
+
progressBar: "_progressBar_16n8k_173",
|
|
21
|
+
inputForm: "_inputForm_16n8k_229",
|
|
22
|
+
prompt: "_prompt_16n8k_247",
|
|
23
|
+
inputField: "_inputField_16n8k_257",
|
|
24
|
+
submitButton: "_submitButton_16n8k_287"
|
|
25
|
+
};
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/Terminal.tsx
|
|
28
|
+
function l({ promptString: l = "user@terminal:~$", placeholder: u = "Type a command...", systemLockedText: d = "System locked during execution...", commandNotFoundFormatter: f = (e) => `Command not found: "${e}".`, initialHistory: p = [], commands: m, title: h = "terminal", showCloseButton: g = !0, onClose: _, headerRightActions: v }) {
|
|
29
|
+
let [y, b] = n(""), [x, S] = n(p), [C, w] = n(!1), [T, E] = n(null), [D, O] = n(""), k = t(null), A = t(null);
|
|
30
|
+
e(() => {
|
|
31
|
+
k.current && (k.current.scrollTop = k.current.scrollHeight);
|
|
32
|
+
}, [
|
|
33
|
+
x,
|
|
34
|
+
T,
|
|
35
|
+
D,
|
|
36
|
+
C
|
|
37
|
+
]);
|
|
38
|
+
let j = () => {
|
|
39
|
+
A.current && A.current.focus();
|
|
40
|
+
}, M = async (e) => {
|
|
41
|
+
e.preventDefault();
|
|
42
|
+
let t = y.trim();
|
|
43
|
+
if (!t || C) return;
|
|
44
|
+
S((e) => [...e, {
|
|
45
|
+
type: "input",
|
|
46
|
+
text: `${l} ${t}`
|
|
47
|
+
}]), b("");
|
|
48
|
+
let n = t.split(" "), r = n[0].toLowerCase();
|
|
49
|
+
if (r === "clear") {
|
|
50
|
+
S([]);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (m && m[r]) {
|
|
54
|
+
w(!0);
|
|
55
|
+
try {
|
|
56
|
+
let e = m[r](n);
|
|
57
|
+
for await (let t of e) t.type === "log" ? S((e) => [...e, t.log]) : t.type === "progress" && (E(t.percent), t.text && O(t.text));
|
|
58
|
+
} catch (e) {
|
|
59
|
+
S((t) => [...t, {
|
|
60
|
+
type: "error",
|
|
61
|
+
text: `Execution error: ${e instanceof Error ? e.message : String(e)}`
|
|
62
|
+
}]);
|
|
63
|
+
} finally {
|
|
64
|
+
w(!1), E(null), O("");
|
|
65
|
+
}
|
|
66
|
+
} else S((e) => [...e, {
|
|
67
|
+
type: "error",
|
|
68
|
+
text: f(r)
|
|
69
|
+
}]);
|
|
70
|
+
}, N = (e) => e === "input" ? c.logInput : e === "error" ? c.logError : e === "success" ? c.logSuccess : c.logOutput;
|
|
71
|
+
return /* @__PURE__ */ s("div", {
|
|
72
|
+
className: c.terminalRoot,
|
|
73
|
+
onClick: j,
|
|
74
|
+
children: [/* @__PURE__ */ s("div", {
|
|
75
|
+
className: c.titleBar,
|
|
76
|
+
children: [/* @__PURE__ */ s("div", {
|
|
77
|
+
className: c.titleLeft,
|
|
78
|
+
children: [/* @__PURE__ */ o(i, {
|
|
79
|
+
className: "w-4 h-4",
|
|
80
|
+
style: { color: "#10b981" }
|
|
81
|
+
}), /* @__PURE__ */ o("span", {
|
|
82
|
+
className: c.titleText,
|
|
83
|
+
children: h
|
|
84
|
+
})]
|
|
85
|
+
}), /* @__PURE__ */ s("div", {
|
|
86
|
+
className: c.titleRight,
|
|
87
|
+
children: [v, g && _ && /* @__PURE__ */ o("button", {
|
|
88
|
+
onClick: (e) => {
|
|
89
|
+
e.stopPropagation(), _();
|
|
90
|
+
},
|
|
91
|
+
className: c.closeButton,
|
|
92
|
+
children: /* @__PURE__ */ o(a, { className: "w-4 h-4" })
|
|
93
|
+
})]
|
|
94
|
+
})]
|
|
95
|
+
}), /* @__PURE__ */ s("div", {
|
|
96
|
+
ref: k,
|
|
97
|
+
className: c.contentBox,
|
|
98
|
+
children: [
|
|
99
|
+
x.map((e, t) => /* @__PURE__ */ o("div", {
|
|
100
|
+
className: `${c.logRow} ${N(e.type)}`,
|
|
101
|
+
children: e.text
|
|
102
|
+
}, t)),
|
|
103
|
+
T !== null && /* @__PURE__ */ s("div", {
|
|
104
|
+
className: c.progressBarContainer,
|
|
105
|
+
children: [/* @__PURE__ */ s("div", {
|
|
106
|
+
className: c.progressHeader,
|
|
107
|
+
children: [/* @__PURE__ */ o("span", { children: D }), /* @__PURE__ */ s("span", { children: [T, "%"] })]
|
|
108
|
+
}), /* @__PURE__ */ o("div", {
|
|
109
|
+
className: c.progressTrack,
|
|
110
|
+
children: /* @__PURE__ */ o("div", {
|
|
111
|
+
className: c.progressBar,
|
|
112
|
+
style: { width: `${T}%` }
|
|
113
|
+
})
|
|
114
|
+
})]
|
|
115
|
+
}),
|
|
116
|
+
/* @__PURE__ */ s("form", {
|
|
117
|
+
onSubmit: M,
|
|
118
|
+
className: c.inputForm,
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ o("span", {
|
|
121
|
+
className: c.prompt,
|
|
122
|
+
children: l
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ o("input", {
|
|
125
|
+
ref: A,
|
|
126
|
+
type: "text",
|
|
127
|
+
value: y,
|
|
128
|
+
onChange: (e) => b(e.target.value),
|
|
129
|
+
disabled: C,
|
|
130
|
+
placeholder: C ? d : u,
|
|
131
|
+
className: c.inputField,
|
|
132
|
+
autoComplete: "off",
|
|
133
|
+
autoCorrect: "off",
|
|
134
|
+
autoCapitalize: "off",
|
|
135
|
+
spellCheck: "false"
|
|
136
|
+
}),
|
|
137
|
+
/* @__PURE__ */ o("button", {
|
|
138
|
+
type: "submit",
|
|
139
|
+
className: c.submitButton,
|
|
140
|
+
children: /* @__PURE__ */ o(r, { className: "w-4 h-4" })
|
|
141
|
+
})
|
|
142
|
+
]
|
|
143
|
+
})
|
|
144
|
+
]
|
|
145
|
+
})]
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { l as Terminal };
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "hakoniwa-term",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "A robust, pluggable retro terminal engine for React, powered by CSS Modules and Async Generators.",
|
|
5
|
+
"main": "./dist/index.cjs",
|
|
6
|
+
"module": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"require": "./dist/index.cjs",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
},
|
|
14
|
+
"./dist/index.css": "./dist/index.css"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "vite build"
|
|
21
|
+
},
|
|
22
|
+
"peerDependencies": {
|
|
23
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
24
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
25
|
+
},
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"lucide-react": "^0.470.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/react": "^19.0.0",
|
|
31
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
32
|
+
"typescript": "^5.9.3",
|
|
33
|
+
"vite": "8.1.3",
|
|
34
|
+
"vite-plugin-dts": "^4.4.0"
|
|
35
|
+
},
|
|
36
|
+
"keywords": [
|
|
37
|
+
"react",
|
|
38
|
+
"terminal",
|
|
39
|
+
"cli-ui",
|
|
40
|
+
"css-modules",
|
|
41
|
+
"async-generator"
|
|
42
|
+
],
|
|
43
|
+
"author": "koji",
|
|
44
|
+
"license": "MIT"
|
|
45
|
+
}
|