vuetty 0.1.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/LICENSE +21 -0
- package/README.md +102 -0
- package/dist/build/bun-loader.d.ts +1 -0
- package/dist/build/bun-loader.js +32 -0
- package/dist/debug/DebugServer.js +1 -0
- package/dist/index.d.ts +79 -0
- package/dist/index.js +1 -0
- package/dist/rollup-plugin/index.d.ts +9 -0
- package/dist/rollup-plugin/index.js +1 -0
- package/dist/static/debug.css +1 -0
- package/dist/static/debug.js +1 -0
- package/dist/static/favicon.ico +0 -0
- package/dist/static/index.html +146 -0
- package/package.json +99 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
class P{constructor(){this.ws=null,this.events=[],this.filters=new Set(["render","input","console","layout","viewport","vue","vuetty"]),this.eventCounts={render:0,input:0,console:0,layout:0,viewport:0,vue:0,vuetty:0},this.searchQuery="",this.reconnectTimeout=null,this.loadPersistedState(),this.connect(),this.setupUI(),this.startTimestampUpdater()}loadPersistedState(){try{let j=localStorage.getItem("debugTheme")||"dark";document.documentElement.setAttribute("data-theme",j);let q=document.getElementById("theme-toggle");if(q)q.checked=j==="dark"}catch(j){}try{let j=localStorage.getItem("debugFilters");if(j){let q=JSON.parse(j);this.filters=new Set(q),document.querySelectorAll("[data-filter]").forEach((A)=>{A.checked=this.filters.has(A.dataset.filter)})}}catch(j){}}saveFilterState(){try{localStorage.setItem("debugFilters",JSON.stringify([...this.filters]))}catch(j){}}saveThemeState(j){try{localStorage.setItem("debugTheme",j)}catch(q){}}connect(){let j=location.protocol==="https:"?"wss:":"ws:";this.ws=new WebSocket(`${j}//${location.host}`),this.ws.onopen=()=>{if(this.updateStatus("Connected","connected"),this.reconnectTimeout)clearTimeout(this.reconnectTimeout),this.reconnectTimeout=null},this.ws.onclose=()=>{this.updateStatus("Disconnected","disconnected"),this.reconnectTimeout=setTimeout(()=>this.connect(),1000)},this.ws.onerror=()=>{this.updateStatus("Error","error")},this.ws.onmessage=(q)=>{try{let A=JSON.parse(q.data);this.handleMessage(A)}catch(A){console.error("Failed to parse message:",A)}}}handleMessage(j){if(j.type==="init"){if(this.events=j.data.buffer||[],this.updateEventCounts(),this.renderEventLog(),j.data.viewport)this.updateViewport(j.data.viewport);if(j.data.layoutTree)this.updateLayoutTree(j.data.layoutTree);if(j.data.memory)this.updateMemoryStats(j.data.memory);return}if(j.type==="cleared"){this.events=[],this.resetEventCounts(),document.getElementById("event-log").innerHTML="";return}if(j.type==="layout.tree"&&j.data?.tree){this.updateLayoutTree(j.data.tree);return}if(j.type==="vuetty.memory"){this.updateMemoryStats(j.data);return}if(this.events.push(j),this.events.length>1000)this.events.shift();let q=j.type.split(".")[0];if(this.eventCounts.hasOwnProperty(q))this.eventCounts[q]++,this.updateCounterDisplay(q);if(this.addEvent(j),j.type==="render.complete"&&j.data.tree)this.updateLayoutTree(j.data.tree);if(j.type.startsWith("viewport.")||j.type==="vuetty.mount"){if(j.data.viewport)this.updateViewport(j.data.viewport)}}updateEventCounts(){this.resetEventCounts(),this.events.forEach((j)=>{let q=j.type.split(".")[0];if(this.eventCounts.hasOwnProperty(q))this.eventCounts[q]++}),this.updateAllCounterDisplays()}resetEventCounts(){Object.keys(this.eventCounts).forEach((j)=>{this.eventCounts[j]=0}),this.updateAllCounterDisplays()}updateCounterDisplay(j){let q=document.querySelector(`[data-counter="${j}"]`);if(q)q.textContent=this.eventCounts[j]}updateAllCounterDisplays(){Object.keys(this.eventCounts).forEach((j)=>{this.updateCounterDisplay(j)})}addEvent(j){let q=j.type.split(".")[0];if(!this.filters.has(q))return;if(this.searchQuery){if(![j.type,this.formatEventData(j).text,JSON.stringify(j.data)].join(" ").toLowerCase().includes(this.searchQuery.toLowerCase()))return}let A=document.getElementById("event-log"),G=document.createElement("div");G.className=`event event-${q}`,G.dataset.timestamp=j.timestamp;let H=document.createElement("span");H.className="time",H.textContent=this.formatRelativeTime(j.timestamp),H.title=new Date(j.timestamp).toLocaleString();let B=document.createElement("span");B.className="type",B.textContent=j.type;let J=document.createElement("span");J.className="data";let K=this.formatEventData(j);if(K.isJSON)try{let M=JSON.stringify(j.data,null,2);J.dataset.compact=K.text,J.dataset.formatted=M,J.textContent="▶ "+K.text}catch{J.textContent=K.text}else J.textContent=K.text;G.appendChild(H),G.appendChild(B),G.appendChild(J),A.appendChild(G),A.scrollTop=A.scrollHeight}formatRelativeTime(j){let A=Date.now()-j;if(A<1000)return"just now";if(A<60000)return`${Math.floor(A/1000)}s ago`;if(A<3600000)return`${Math.floor(A/60000)}m ago`;if(A<86400000)return`${Math.floor(A/3600000)}h ago`;return`${Math.floor(A/86400000)}d ago`}startTimestampUpdater(){setInterval(()=>{document.querySelectorAll(".event").forEach((j)=>{let q=parseInt(j.dataset.timestamp);if(q){let A=j.querySelector(".time");if(A)A.textContent=this.formatRelativeTime(q)}})},1e4)}formatEventData(j){switch(j.type){case"render.complete":if(j.data.duration!==void 0)return{text:`${j.data.duration.toFixed(2)}ms`,isJSON:!1};return{text:"",isJSON:!1};case"input.key":return{text:j.data.char||j.data.key||"",isJSON:!1};case"console.log":case"console.error":case"console.warn":return{text:(j.data.args||[]).join(" "),isJSON:!1};case"vue.warn":case"vue.error":return{text:`[${j.data.component}] ${j.data.message}`,isJSON:!1};case"vuetty.resize":return{text:`${j.data.width}x${j.data.height}`,isJSON:!1};default:try{return{text:JSON.stringify(j.data),isJSON:!0}}catch{return{text:"",isJSON:!1}}}}updateLayoutTree(j){let q=document.getElementById("layout-tree");if(j)q.textContent=JSON.stringify(j,null,2)}updateViewport(j){let q=document.getElementById("viewport-state");if(j)q.textContent=JSON.stringify(j,null,2)}updateMemoryStats(j){let q=document.getElementById("memory-stats");if(q&&j)q.textContent=JSON.stringify(j,null,2)}updateStatus(j,q){let A=document.getElementById("connection-status");switch(A.textContent=j,A.classList.remove("is-success","is-danger","is-warning","is-info"),q){case"connected":A.classList.add("is-success");break;case"disconnected":A.classList.add("is-warning");break;case"error":A.classList.add("is-danger");break;default:A.classList.add("is-info")}}renderEventLog(){let j=document.getElementById("event-log");j.innerHTML="",this.events.forEach((q)=>this.addEvent(q))}setupUI(){document.getElementById("event-log").addEventListener("click",(G)=>{let H=G.target.closest(".event");if(!H)return;let B=H.querySelector(".data");if(!B)return;let J=B.classList.toggle("expanded");if(B.dataset.compact&&B.dataset.formatted)if(J)B.textContent="▼ "+B.dataset.formatted;else B.textContent="▶ "+B.dataset.compact;else{let K=B.textContent;if(J)B.textContent=K.replace("▶ ","▼ ");else B.textContent=K.replace("▼ ","▶ ")}}),document.querySelectorAll("[data-filter]").forEach((G)=>{G.addEventListener("change",(H)=>{let B=H.target.dataset.filter;if(H.target.checked)this.filters.add(B);else this.filters.delete(B);this.saveFilterState(),this.renderEventLog()})});let q=document.getElementById("search-input");if(q)q.addEventListener("input",(G)=>{this.searchQuery=G.target.value,this.renderEventLog()});let A=document.getElementById("theme-toggle");if(A)A.addEventListener("change",(G)=>{let H=G.target.checked?"dark":"light";document.documentElement.setAttribute("data-theme",H),this.saveThemeState(H)});document.getElementById("clear-btn").addEventListener("click",()=>{if(this.ws&&this.ws.readyState===WebSocket.OPEN)this.ws.send(JSON.stringify({type:"clear"}));this.events=[],this.resetEventCounts(),document.getElementById("event-log").innerHTML=""}),document.querySelectorAll(".tabs li").forEach((G)=>{G.addEventListener("click",(H)=>{H.preventDefault();let B=G.dataset.tab;document.querySelectorAll(".tabs li").forEach((M)=>{M.classList.remove("is-active");let O=M.querySelector("a");if(O)O.style.color="var(--text-secondary)"}),G.classList.add("is-active");let J=G.querySelector("a");if(J)J.style.color="var(--text-primary)";document.querySelectorAll(".tab-content").forEach((M)=>{M.style.display="none",M.classList.remove("is-active")});let K=document.getElementById(`${B}-tab`);if(K)K.style.display="block",K.classList.add("is-active")})})}}new P;
|
|
Binary file
|
|
@@ -0,0 +1,146 @@
|
|
|
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>Vuetty Debug Server</title>
|
|
7
|
+
<link rel="stylesheet" href="debug.css">
|
|
8
|
+
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
|
9
|
+
</head>
|
|
10
|
+
<body>
|
|
11
|
+
<!-- Main container -->
|
|
12
|
+
<div style="height: 100vh; display: flex; flex-direction: column;">
|
|
13
|
+
|
|
14
|
+
<!-- Header -->
|
|
15
|
+
<header class="navbar" role="navigation" style="background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); flex-shrink: 0;">
|
|
16
|
+
<div class="navbar-brand">
|
|
17
|
+
<h1 class="navbar-item title is-5 mb-0" style="color: var(--event-render);">Vuetty Debug Server</h1>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<div class="navbar-menu" id="navbarMenu" style="background: transparent;">
|
|
21
|
+
<div class="navbar-end">
|
|
22
|
+
<!-- Connection status -->
|
|
23
|
+
<div class="navbar-item">
|
|
24
|
+
<span id="connection-status" class="tag is-info">Connecting...</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<!-- Theme toggle -->
|
|
28
|
+
<div class="navbar-item">
|
|
29
|
+
<div class="theme-toggle-wrapper">
|
|
30
|
+
<span class="icon">☀️</span>
|
|
31
|
+
<input type="checkbox" id="theme-toggle" checked>
|
|
32
|
+
<span class="icon">🌙</span>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<!-- Clear button -->
|
|
37
|
+
<div class="navbar-item">
|
|
38
|
+
<button id="clear-btn" class="button is-primary is-small">Clear</button>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</header>
|
|
43
|
+
|
|
44
|
+
<!-- Main content panels -->
|
|
45
|
+
<div class="columns is-gapless" style="flex: 1; overflow: hidden; margin: 0;">
|
|
46
|
+
|
|
47
|
+
<!-- Left Panel: Event Log -->
|
|
48
|
+
<div class="column is-two-thirds" style="display: flex; flex-direction: column; border-right: 1px solid var(--border-color); background: var(--bg-panel);">
|
|
49
|
+
|
|
50
|
+
<!-- Panel header -->
|
|
51
|
+
<div class="panel-heading" style="background: var(--bg-tertiary); border-bottom: 1px solid var(--border-color); flex-shrink: 0;">
|
|
52
|
+
<h2 class="title is-6 mb-0" style="color: var(--text-primary);">Event Log</h2>
|
|
53
|
+
</div>
|
|
54
|
+
|
|
55
|
+
<!-- Search filter -->
|
|
56
|
+
<div class="panel-block" style="border-bottom: 1px solid var(--border-color); background: var(--bg-secondary); flex-shrink: 0;">
|
|
57
|
+
<div class="control has-icons-left" style="width: 100%;">
|
|
58
|
+
<input
|
|
59
|
+
type="text"
|
|
60
|
+
id="search-input"
|
|
61
|
+
class="input is-small"
|
|
62
|
+
placeholder="Search events..."
|
|
63
|
+
style="background: var(--bg-primary); color: var(--text-primary); border-color: var(--border-color);"
|
|
64
|
+
>
|
|
65
|
+
<span class="icon is-left is-small">
|
|
66
|
+
<span style="font-size: 14px;">🔍</span>
|
|
67
|
+
</span>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<!-- Event filters (checkboxes) -->
|
|
72
|
+
<div class="panel-block" style="flex-wrap: wrap; gap: 8px; padding: 10px; background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); flex-shrink: 0;">
|
|
73
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
74
|
+
<input type="checkbox" checked data-filter="render">
|
|
75
|
+
Render<span class="event-counter" data-counter="render">0</span>
|
|
76
|
+
</label>
|
|
77
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
78
|
+
<input type="checkbox" checked data-filter="input">
|
|
79
|
+
Input<span class="event-counter" data-counter="input">0</span>
|
|
80
|
+
</label>
|
|
81
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
82
|
+
<input type="checkbox" checked data-filter="console">
|
|
83
|
+
Console<span class="event-counter" data-counter="console">0</span>
|
|
84
|
+
</label>
|
|
85
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
86
|
+
<input type="checkbox" checked data-filter="layout">
|
|
87
|
+
Layout<span class="event-counter" data-counter="layout">0</span>
|
|
88
|
+
</label>
|
|
89
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
90
|
+
<input type="checkbox" checked data-filter="viewport">
|
|
91
|
+
Viewport<span class="event-counter" data-counter="viewport">0</span>
|
|
92
|
+
</label>
|
|
93
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
94
|
+
<input type="checkbox" checked data-filter="vue">
|
|
95
|
+
Vue<span class="event-counter" data-counter="vue">0</span>
|
|
96
|
+
</label>
|
|
97
|
+
<label class="checkbox" style="font-size: 12px; margin-right: 8px; color: var(--text-primary);">
|
|
98
|
+
<input type="checkbox" checked data-filter="vuetty">
|
|
99
|
+
Vuetty<span class="event-counter" data-counter="vuetty">0</span>
|
|
100
|
+
</label>
|
|
101
|
+
</div>
|
|
102
|
+
|
|
103
|
+
<!-- Event list -->
|
|
104
|
+
<div id="event-log" style="flex: 1; overflow-y: auto; padding: 10px; background: var(--bg-primary);"></div>
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
<!-- Right Panel: Details -->
|
|
108
|
+
<div class="column is-one-third" style="display: flex; flex-direction: column; background: var(--bg-panel);">
|
|
109
|
+
|
|
110
|
+
<!-- Tabs -->
|
|
111
|
+
<div class="tabs" style="margin-bottom: 0; background: var(--bg-tertiary); border-bottom: 1px solid var(--border-color); flex-shrink: 0;">
|
|
112
|
+
<ul style="border-bottom: none; margin: 0;">
|
|
113
|
+
<li class="is-active" data-tab="layout">
|
|
114
|
+
<a style="color: var(--text-primary);">Layout Tree</a>
|
|
115
|
+
</li>
|
|
116
|
+
<li data-tab="viewport">
|
|
117
|
+
<a style="color: var(--text-secondary);">Viewport</a>
|
|
118
|
+
</li>
|
|
119
|
+
<li data-tab="memory">
|
|
120
|
+
<a style="color: var(--text-secondary);">Memory</a>
|
|
121
|
+
</li>
|
|
122
|
+
</ul>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<!-- Tab content -->
|
|
126
|
+
<div style="flex: 1; overflow-y: auto; padding: 10px; background: var(--bg-primary);">
|
|
127
|
+
<div id="layout-tab" class="tab-content is-active">
|
|
128
|
+
<pre id="layout-tree">No layout data yet...</pre>
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
<div id="viewport-tab" class="tab-content" style="display: none;">
|
|
132
|
+
<pre id="viewport-state">No viewport data yet...</pre>
|
|
133
|
+
</div>
|
|
134
|
+
|
|
135
|
+
<div id="memory-tab" class="tab-content" style="display: none;">
|
|
136
|
+
<pre id="memory-stats">No memory data yet...</pre>
|
|
137
|
+
</div>
|
|
138
|
+
</div>
|
|
139
|
+
</div>
|
|
140
|
+
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
|
|
144
|
+
<script src="debug.js"></script>
|
|
145
|
+
</body>
|
|
146
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vuetty",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Build reactive Terminal User Interfaces using Vue.js",
|
|
5
|
+
"homepage": "https://github.com/tterrasson/vuetty",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/tterrasson/vuetty.git"
|
|
9
|
+
},
|
|
10
|
+
"bugs": {
|
|
11
|
+
"url": "https://github.com/tterrasson/vuetty/issues"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"module": "./dist/index.js",
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"import": "./dist/index.js",
|
|
21
|
+
"default": "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
"./rollup-plugin": {
|
|
24
|
+
"types": "./dist/rollup-plugin/index.d.ts",
|
|
25
|
+
"import": "./dist/rollup-plugin/index.js",
|
|
26
|
+
"default": "./dist/rollup-plugin/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./bun-loader": {
|
|
29
|
+
"types": "./dist/build/bun-loader.d.ts",
|
|
30
|
+
"import": "./dist/build/bun-loader.js",
|
|
31
|
+
"default": "./dist/build/bun-loader.js"
|
|
32
|
+
},
|
|
33
|
+
"./loader": {
|
|
34
|
+
"types": "./dist/build/bun-loader.d.ts",
|
|
35
|
+
"import": "./dist/build/bun-loader.js",
|
|
36
|
+
"default": "./dist/build/bun-loader.js"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"files": [
|
|
40
|
+
"dist",
|
|
41
|
+
"README.md",
|
|
42
|
+
"LICENSE"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "bun run clean && bun run build:rollup && bun run build:types && bun run build:debug-assets",
|
|
46
|
+
"build:rollup": "rollup -c rollup.config.js",
|
|
47
|
+
"build:types": "cp src/index.d.ts dist/ && mkdir -p dist/rollup-plugin && cp src/rollup-plugin/index.d.ts dist/rollup-plugin/ && mkdir -p dist/build && cp src/build/bun-loader.d.ts src/build/bun-loader.js dist/build/",
|
|
48
|
+
"build:debug-assets": "bun run scripts/build-debug.js",
|
|
49
|
+
"clean": "rm -rf ./dist && mkdir -p ./dist",
|
|
50
|
+
"dev": "bun run examples/run.js",
|
|
51
|
+
"test": "bun test",
|
|
52
|
+
"pack": "bun run build && bunx npm pack",
|
|
53
|
+
"prepublishOnly": "bun run build",
|
|
54
|
+
"docs:dev": "bun vitepress dev docs",
|
|
55
|
+
"docs:build": "bun vitepress build docs",
|
|
56
|
+
"docs:preview": "bun vitepress preview docs"
|
|
57
|
+
},
|
|
58
|
+
"keywords": [
|
|
59
|
+
"vuetty",
|
|
60
|
+
"tui",
|
|
61
|
+
"terminal",
|
|
62
|
+
"vue",
|
|
63
|
+
"vue3",
|
|
64
|
+
"custom-renderer",
|
|
65
|
+
"cli",
|
|
66
|
+
"terminal-ui",
|
|
67
|
+
"reactive",
|
|
68
|
+
"text-ui",
|
|
69
|
+
"command-line",
|
|
70
|
+
"flexbox"
|
|
71
|
+
],
|
|
72
|
+
"author": "Thibault Terrasson",
|
|
73
|
+
"license": "MIT",
|
|
74
|
+
"peerDependencies": {
|
|
75
|
+
"vue": "^3.5.26"
|
|
76
|
+
},
|
|
77
|
+
"dependencies": {
|
|
78
|
+
"chalk": "^5.6.2",
|
|
79
|
+
"cli-highlight": "^2.1.11",
|
|
80
|
+
"figlet": "^1.9.4",
|
|
81
|
+
"glob": "^13.0.0",
|
|
82
|
+
"gradient-string": "^3.0.0",
|
|
83
|
+
"marked": "^17.0.1",
|
|
84
|
+
"string-width": "^8.1.0",
|
|
85
|
+
"terminal-image": "^4.2.0",
|
|
86
|
+
"yoga-layout": "^3.2.1"
|
|
87
|
+
},
|
|
88
|
+
"devDependencies": {
|
|
89
|
+
"@rollup/plugin-alias": "^6.0.0",
|
|
90
|
+
"@rollup/plugin-commonjs": "^29.0.0",
|
|
91
|
+
"@rollup/plugin-node-resolve": "^16.0.3",
|
|
92
|
+
"@rollup/plugin-terser": "^0.4.4",
|
|
93
|
+
"@types/bun": "latest",
|
|
94
|
+
"@vue/compiler-sfc": "^3.5.27",
|
|
95
|
+
"bulma": "^1.0.4",
|
|
96
|
+
"rollup": "^4.55.2",
|
|
97
|
+
"vitepress": "^2.0.0-alpha.15"
|
|
98
|
+
}
|
|
99
|
+
}
|