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 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
+ ![screenshot](resources/screenshot_hakoniwa_term.png)
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*/
@@ -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
+ }