murasaki 0.2.0 โ 0.3.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/bin/murasaki.js +8 -3
- package/dist/cli/colors.d.ts +13 -0
- package/dist/cli/colors.d.ts.map +1 -0
- package/dist/cli/colors.js +14 -0
- package/dist/cli/colors.js.map +1 -0
- package/dist/cli/log.d.ts +14 -0
- package/dist/cli/log.d.ts.map +1 -0
- package/dist/cli/log.js +25 -0
- package/dist/cli/log.js.map +1 -0
- package/dist/components/Link.d.ts +9 -0
- package/dist/components/Link.d.ts.map +1 -0
- package/dist/components/Link.js +15 -0
- package/dist/components/Link.js.map +1 -0
- package/dist/dev.d.ts +2 -0
- package/dist/dev.d.ts.map +1 -0
- package/dist/dev.js +41 -0
- package/dist/dev.js.map +1 -0
- package/dist/env.d.ts +15 -0
- package/dist/env.d.ts.map +1 -0
- package/dist/env.js +43 -0
- package/dist/env.js.map +1 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/jsx/dom/index.d.ts +2 -0
- package/dist/jsx/dom/index.d.ts.map +1 -0
- package/dist/jsx/dom/index.js +3 -0
- package/dist/jsx/dom/index.js.map +1 -0
- package/dist/jsx/dom/runtime.d.ts +34 -0
- package/dist/jsx/dom/runtime.d.ts.map +1 -0
- package/dist/jsx/dom/runtime.js +303 -0
- package/dist/jsx/dom/runtime.js.map +1 -0
- package/dist/jsx/index.d.ts +3 -0
- package/dist/jsx/index.d.ts.map +1 -0
- package/dist/jsx/index.js +3 -0
- package/dist/jsx/index.js.map +1 -0
- package/dist/jsx/jsx-dev-runtime.d.ts +2 -0
- package/dist/jsx/jsx-dev-runtime.d.ts.map +1 -0
- package/dist/jsx/jsx-dev-runtime.js +7 -0
- package/dist/jsx/jsx-dev-runtime.js.map +1 -0
- package/dist/jsx/jsx-runtime.d.ts +2 -0
- package/dist/jsx/jsx-runtime.d.ts.map +1 -0
- package/{src/jsx/jsx-runtime.ts โ dist/jsx/jsx-runtime.js} +4 -3
- package/dist/jsx/jsx-runtime.js.map +1 -0
- package/dist/jsx/runtime.d.ts +24 -0
- package/dist/jsx/runtime.d.ts.map +1 -0
- package/dist/jsx/runtime.js +273 -0
- package/dist/jsx/runtime.js.map +1 -0
- package/dist/jsx/types.d.ts +30 -0
- package/dist/jsx/types.d.ts.map +1 -0
- package/dist/jsx/types.js +3 -0
- package/dist/jsx/types.js.map +1 -0
- package/dist/runtime/bundle.d.ts +8 -0
- package/dist/runtime/bundle.d.ts.map +1 -0
- package/dist/runtime/bundle.js +98 -0
- package/dist/runtime/bundle.js.map +1 -0
- package/dist/runtime/hmr.d.ts +2 -0
- package/dist/runtime/hmr.d.ts.map +1 -0
- package/dist/runtime/hmr.js +28 -0
- package/dist/runtime/hmr.js.map +1 -0
- package/dist/runtime/render.d.ts +3 -0
- package/dist/runtime/render.d.ts.map +1 -0
- package/dist/runtime/render.js +226 -0
- package/dist/runtime/render.js.map +1 -0
- package/dist/runtime/routes.d.ts +10 -0
- package/dist/runtime/routes.d.ts.map +1 -0
- package/dist/runtime/routes.js +65 -0
- package/dist/runtime/routes.js.map +1 -0
- package/dist/runtime/shortcuts.d.ts +8 -0
- package/dist/runtime/shortcuts.d.ts.map +1 -0
- package/dist/runtime/shortcuts.js +29 -0
- package/dist/runtime/shortcuts.js.map +1 -0
- package/dist/runtime/window.d.ts +10 -0
- package/dist/runtime/window.d.ts.map +1 -0
- package/dist/runtime/window.js +93 -0
- package/dist/runtime/window.js.map +1 -0
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/package.json +17 -10
- package/src/cli/colors.ts +0 -16
- package/src/cli/log.ts +0 -42
- package/src/components/Link.tsx +0 -25
- package/src/dev.tsx +0 -60
- package/src/env.ts +0 -48
- package/src/index.ts +0 -24
- package/src/jsx/index.ts +0 -21
- package/src/jsx/jsx-dev-runtime.ts +0 -6
- package/src/jsx/runtime.ts +0 -298
- package/src/jsx/types.ts +0 -36
- package/src/runtime/hmr.ts +0 -26
- package/src/runtime/render.tsx +0 -225
- package/src/runtime/routes.ts +0 -73
- package/src/runtime/shortcuts.ts +0 -31
- package/src/runtime/window.ts +0 -94
- package/src/types.ts +0 -22
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
// Multi-route renderer.
|
|
2
|
+
//
|
|
3
|
+
// Pipeline:
|
|
4
|
+
// 1. discover all src/app/<...>/page.tsx
|
|
5
|
+
// 2. render each page wrapped in its nested layouts (NOT the root layout)
|
|
6
|
+
// 3. render the root layout once with a switcher block as its children
|
|
7
|
+
// 4. inject metadata (<title>, <meta description>) + globals.css into <head>
|
|
8
|
+
// 5. inject a tiny navigation script that listens to hash changes and
|
|
9
|
+
// intercepts <Link> clicks
|
|
10
|
+
//
|
|
11
|
+
// Legacy single-page (src/app.tsx + src/layout.tsx) is still supported:
|
|
12
|
+
// if src/app/ doesn't exist, we render the legacy convention.
|
|
13
|
+
var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
|
|
14
|
+
if (typeof path === "string" && /^\.\.?\//.test(path)) {
|
|
15
|
+
return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
|
|
16
|
+
return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
return path;
|
|
20
|
+
};
|
|
21
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
22
|
+
import { pathToFileURL } from 'node:url';
|
|
23
|
+
import { APP_DIR, APP_GLOBALS_CSS, LEGACY_APP_PATH, LEGACY_GLOBALS_CSS, LEGACY_LAYOUT_PATH, } from "../env.js";
|
|
24
|
+
import { jsx, raw, renderToString } from "../jsx/runtime.js";
|
|
25
|
+
import { bundleClient } from "./bundle.js";
|
|
26
|
+
import { discoverRoutes } from "./routes.js";
|
|
27
|
+
const NO_APP_HTML = '<!doctype html><html><body style="font-family:system-ui;padding:40px;">' +
|
|
28
|
+
'<h1 style="color:#A855F7">No app found</h1>' +
|
|
29
|
+
'<p>Create <code>src/app/page.tsx</code> and the window will reload.</p></body></html>';
|
|
30
|
+
function escapeHtml(s) {
|
|
31
|
+
return s
|
|
32
|
+
.replace(/&/g, '&')
|
|
33
|
+
.replace(/</g, '<')
|
|
34
|
+
.replace(/>/g, '>')
|
|
35
|
+
.replace(/"/g, '"');
|
|
36
|
+
}
|
|
37
|
+
async function dynImport(path) {
|
|
38
|
+
const url = pathToFileURL(path).href + `?v=${Date.now()}`;
|
|
39
|
+
return import(__rewriteRelativeImportExtension(url));
|
|
40
|
+
}
|
|
41
|
+
async function loadModule(path) {
|
|
42
|
+
if (!existsSync(path))
|
|
43
|
+
return null;
|
|
44
|
+
const mod = await dynImport(path);
|
|
45
|
+
if (!mod.default)
|
|
46
|
+
return null;
|
|
47
|
+
return mod;
|
|
48
|
+
}
|
|
49
|
+
// โโ Page rendering โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
50
|
+
async function renderPageInner(route, rootLayoutFile) {
|
|
51
|
+
const pageMod = await loadModule(route.pageFile);
|
|
52
|
+
if (!pageMod)
|
|
53
|
+
return '';
|
|
54
|
+
let tree = jsx(pageMod.default, null);
|
|
55
|
+
// Wrap in nested layouts, innermost first (i.e. skip root layout โ it wraps everything later)
|
|
56
|
+
// route.layoutFiles: outermost first โ so iterate from end backwards excluding root
|
|
57
|
+
const layoutsToApply = route.layoutFiles.filter((f) => f !== rootLayoutFile);
|
|
58
|
+
// Apply from innermost (last in array) outward (first in array) so the
|
|
59
|
+
// outer layout wraps the inner: <Outer><Inner><Page/></Inner></Outer>
|
|
60
|
+
for (let i = layoutsToApply.length - 1; i >= 0; i--) {
|
|
61
|
+
const layoutMod = await loadModule(layoutsToApply[i]);
|
|
62
|
+
if (!layoutMod)
|
|
63
|
+
continue;
|
|
64
|
+
tree = jsx(layoutMod.default, { children: tree });
|
|
65
|
+
}
|
|
66
|
+
return renderToString(tree);
|
|
67
|
+
}
|
|
68
|
+
// โโ Root layout + metadata โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
69
|
+
async function renderRootLayout(rootLayout, body) {
|
|
70
|
+
if (!rootLayout) {
|
|
71
|
+
// Fallback root if user didn't write src/app/layout.tsx
|
|
72
|
+
const fallback = jsx('html', {
|
|
73
|
+
lang: 'en',
|
|
74
|
+
children: [
|
|
75
|
+
jsx('head', { children: jsx('meta', { charSet: 'utf-8' }) }),
|
|
76
|
+
jsx('body', { children: body }),
|
|
77
|
+
],
|
|
78
|
+
});
|
|
79
|
+
return renderToString(fallback);
|
|
80
|
+
}
|
|
81
|
+
const tree = jsx(rootLayout.default, { children: body });
|
|
82
|
+
return renderToString(tree);
|
|
83
|
+
}
|
|
84
|
+
// โโ Navigation script (injected once) โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
85
|
+
const NAV_SCRIPT = `
|
|
86
|
+
<script>
|
|
87
|
+
(function(){
|
|
88
|
+
function showRoute(path){
|
|
89
|
+
var blocks=document.querySelectorAll('[data-murasaki-route]');
|
|
90
|
+
var matched=false;
|
|
91
|
+
for(var i=0;i<blocks.length;i++){
|
|
92
|
+
var b=blocks[i];
|
|
93
|
+
if(b.getAttribute('data-murasaki-route')===path){
|
|
94
|
+
b.removeAttribute('hidden');matched=true;
|
|
95
|
+
} else {
|
|
96
|
+
b.setAttribute('hidden','');
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if(!matched){
|
|
100
|
+
// fallback to "/"
|
|
101
|
+
var root=document.querySelector('[data-murasaki-route="/"]');
|
|
102
|
+
if(root)root.removeAttribute('hidden');
|
|
103
|
+
}
|
|
104
|
+
document.dispatchEvent(new CustomEvent('murasaki:navigate',{detail:{path:path}}));
|
|
105
|
+
}
|
|
106
|
+
function currentPath(){
|
|
107
|
+
var h=location.hash||'';
|
|
108
|
+
return h.charAt(0)==='#'?h.slice(1)||'/':'/'
|
|
109
|
+
}
|
|
110
|
+
window.addEventListener('hashchange',function(){showRoute(currentPath())});
|
|
111
|
+
document.addEventListener('click',function(e){
|
|
112
|
+
var t=e.target;
|
|
113
|
+
while(t&&t.nodeType===1){
|
|
114
|
+
if(t.tagName==='A'&&t.hasAttribute('data-murasaki-link')){
|
|
115
|
+
e.preventDefault();
|
|
116
|
+
var href=t.getAttribute('data-murasaki-link');
|
|
117
|
+
if('#'+href!==location.hash){location.hash=href;}
|
|
118
|
+
else{showRoute(href);}
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
t=t.parentNode;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Initial render
|
|
125
|
+
showRoute(currentPath());
|
|
126
|
+
})();
|
|
127
|
+
</script>
|
|
128
|
+
`.trim();
|
|
129
|
+
// โโ Globals.css discovery (app/ takes precedence over src/) โโโโโโโโโ
|
|
130
|
+
function loadGlobalsCss() {
|
|
131
|
+
for (const p of [APP_GLOBALS_CSS, LEGACY_GLOBALS_CSS]) {
|
|
132
|
+
if (existsSync(p)) {
|
|
133
|
+
try {
|
|
134
|
+
return readFileSync(p, 'utf8');
|
|
135
|
+
}
|
|
136
|
+
catch { }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return '';
|
|
140
|
+
}
|
|
141
|
+
// โโ Head injection โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
142
|
+
function injectHead(html, metadata, css) {
|
|
143
|
+
const headInjects = [];
|
|
144
|
+
if (metadata?.title && !/<title>.*?<\/title>/i.test(html)) {
|
|
145
|
+
headInjects.push(`<title>${escapeHtml(metadata.title)}</title>`);
|
|
146
|
+
}
|
|
147
|
+
if (metadata?.description && !/<meta[^>]+name=["']description["']/i.test(html)) {
|
|
148
|
+
headInjects.push(`<meta name="description" content="${escapeHtml(metadata.description)}">`);
|
|
149
|
+
}
|
|
150
|
+
if (css) {
|
|
151
|
+
headInjects.push(`<style data-murasaki="globals.css">${css}</style>`);
|
|
152
|
+
}
|
|
153
|
+
if (!headInjects.length)
|
|
154
|
+
return html;
|
|
155
|
+
const blob = headInjects.join('');
|
|
156
|
+
if (html.includes('</head>'))
|
|
157
|
+
return html.replace('</head>', blob + '</head>');
|
|
158
|
+
return html.replace('<body', blob + '<body');
|
|
159
|
+
}
|
|
160
|
+
// โโ Main entry โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
161
|
+
export async function renderApp() {
|
|
162
|
+
// 1. Try app-router convention first
|
|
163
|
+
if (existsSync(APP_DIR)) {
|
|
164
|
+
return renderAppRouter();
|
|
165
|
+
}
|
|
166
|
+
// 2. Fall back to legacy single-page
|
|
167
|
+
if (existsSync(LEGACY_APP_PATH)) {
|
|
168
|
+
return renderLegacy();
|
|
169
|
+
}
|
|
170
|
+
return { html: NO_APP_HTML };
|
|
171
|
+
}
|
|
172
|
+
async function renderAppRouter() {
|
|
173
|
+
const routes = discoverRoutes(APP_DIR);
|
|
174
|
+
if (routes.length === 0)
|
|
175
|
+
return { html: NO_APP_HTML };
|
|
176
|
+
// Identify root layout (src/app/layout.tsx) if any
|
|
177
|
+
const rootLayoutPath = routes[0]?.layoutFiles[0];
|
|
178
|
+
const rootLayoutFile = rootLayoutPath && rootLayoutPath.endsWith('/app/layout.tsx') ? rootLayoutPath : null;
|
|
179
|
+
const rootLayoutMod = rootLayoutFile ? await loadModule(rootLayoutFile) : null;
|
|
180
|
+
const metadata = rootLayoutMod?.metadata;
|
|
181
|
+
// 2. Server pre-render each page (gives a usable first paint before the
|
|
182
|
+
// client bundle boots โ pre-hydration shell). Each block goes inside
|
|
183
|
+
// the mount root and is replaced by the client renderer on boot.
|
|
184
|
+
const blocks = [];
|
|
185
|
+
for (const route of routes) {
|
|
186
|
+
const inner = await renderPageInner(route, rootLayoutFile);
|
|
187
|
+
blocks.push(`<div data-murasaki-route="${escapeHtml(route.path)}" hidden>${inner}</div>`);
|
|
188
|
+
}
|
|
189
|
+
// 3. Wrap the server-rendered content + nav script in the mount container.
|
|
190
|
+
// The client bundle will clear this container on boot and re-render
|
|
191
|
+
// interactively (useState/onClick actually fire after that).
|
|
192
|
+
const mountInner = blocks.join('') + NAV_SCRIPT;
|
|
193
|
+
const mountRoot = `<div id="murasaki-root">${mountInner}</div>`;
|
|
194
|
+
// 4. Build + inline client bundle (esbuild, IIFE)
|
|
195
|
+
let clientScript = '';
|
|
196
|
+
try {
|
|
197
|
+
const code = await bundleClient({ routes, rootLayoutFile });
|
|
198
|
+
clientScript = `<script type="module" data-murasaki="client">${code}</script>`;
|
|
199
|
+
}
|
|
200
|
+
catch (e) {
|
|
201
|
+
// Render an inline error overlay so the WebView shows what broke
|
|
202
|
+
const msg = escapeHtml(String(e?.message ?? e));
|
|
203
|
+
clientScript = `<script>document.body.insertAdjacentHTML('beforeend','<pre style=\\'position:fixed;inset:0;background:#1a0a33;color:#A855F7;padding:24px;font-family:SF Mono,Menlo,monospace;font-size:13px;overflow:auto;z-index:9999\\'>client bundle failed:\\n${msg.replace(/[\\'\\\\]/g, '\\\\$&')}</pre>')</script>`;
|
|
204
|
+
}
|
|
205
|
+
// 5. Render root layout with mount root + client script as children
|
|
206
|
+
const bodyContent = await renderRootLayout(rootLayoutMod, raw(mountRoot + clientScript));
|
|
207
|
+
let html = '<!doctype html>' + bodyContent;
|
|
208
|
+
// 6. Inject metadata + globals.css
|
|
209
|
+
html = injectHead(html, metadata, loadGlobalsCss());
|
|
210
|
+
return { html, metadata };
|
|
211
|
+
}
|
|
212
|
+
// โโ Legacy single-page render (unchanged shape) โโโโโโโโโโโโโโโโโโโโโ
|
|
213
|
+
async function renderLegacy() {
|
|
214
|
+
const pageMod = await loadModule(LEGACY_APP_PATH);
|
|
215
|
+
if (!pageMod)
|
|
216
|
+
return { html: NO_APP_HTML };
|
|
217
|
+
const layoutMod = await loadModule(LEGACY_LAYOUT_PATH);
|
|
218
|
+
const metadata = layoutMod?.metadata;
|
|
219
|
+
const body = layoutMod
|
|
220
|
+
? jsx(layoutMod.default, { children: jsx(pageMod.default, null) })
|
|
221
|
+
: jsx(pageMod.default, null);
|
|
222
|
+
let html = '<!doctype html>' + renderToString(body);
|
|
223
|
+
html = injectHead(html, metadata, loadGlobalsCss());
|
|
224
|
+
return { html, metadata };
|
|
225
|
+
}
|
|
226
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/runtime/render.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type Route = {
|
|
2
|
+
/** URL-style path: "/", "/about", "/settings/profile" */
|
|
3
|
+
path: string;
|
|
4
|
+
/** Absolute path to the page.tsx file */
|
|
5
|
+
pageFile: string;
|
|
6
|
+
/** Absolute paths to layout.tsx files, OUTERMOST first (root โ innermost) */
|
|
7
|
+
layoutFiles: string[];
|
|
8
|
+
};
|
|
9
|
+
export declare function discoverRoutes(appDir: string): Route[];
|
|
10
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/runtime/routes.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,KAAK,GAAG;IAClB,yDAAyD;IACzD,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,6EAA6E;IAC7E,WAAW,EAAE,MAAM,EAAE,CAAA;CACtB,CAAA;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,KAAK,EAAE,CAWtD"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// File-based route discovery for src/app/.
|
|
2
|
+
//
|
|
3
|
+
// Conventions (subset of Next.js app router):
|
|
4
|
+
// src/app/page.tsx โ "/"
|
|
5
|
+
// src/app/layout.tsx โ root layout (wraps everything)
|
|
6
|
+
// src/app/about/page.tsx โ "/about"
|
|
7
|
+
// src/app/about/layout.tsx โ nested layout for /about and its children
|
|
8
|
+
// src/app/_foo/ โ ignored (leading underscore = private)
|
|
9
|
+
//
|
|
10
|
+
// A page.tsx is required to register a route. A layout.tsx without a
|
|
11
|
+
// page.tsx in the same dir just contributes to children's layouts.
|
|
12
|
+
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
export function discoverRoutes(appDir) {
|
|
15
|
+
if (!existsSync(appDir))
|
|
16
|
+
return [];
|
|
17
|
+
const out = [];
|
|
18
|
+
walk(appDir, '', [], out);
|
|
19
|
+
// Stable sort so "/" comes first, then alphabetical
|
|
20
|
+
out.sort((a, b) => {
|
|
21
|
+
if (a.path === '/')
|
|
22
|
+
return -1;
|
|
23
|
+
if (b.path === '/')
|
|
24
|
+
return 1;
|
|
25
|
+
return a.path.localeCompare(b.path);
|
|
26
|
+
});
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
29
|
+
function walk(dir, routePath, layouts, out) {
|
|
30
|
+
let entries;
|
|
31
|
+
try {
|
|
32
|
+
entries = readdirSync(dir);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const hasLayout = entries.includes('layout.tsx');
|
|
38
|
+
const hasPage = entries.includes('page.tsx');
|
|
39
|
+
// The current directory contributes its layout to itself and descendants.
|
|
40
|
+
const ownLayouts = hasLayout ? [...layouts, join(dir, 'layout.tsx')] : layouts;
|
|
41
|
+
if (hasPage) {
|
|
42
|
+
out.push({
|
|
43
|
+
path: routePath || '/',
|
|
44
|
+
pageFile: join(dir, 'page.tsx'),
|
|
45
|
+
layoutFiles: ownLayouts,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
// Recurse into subdirectories (skip files, hidden, private "_*", node_modules).
|
|
49
|
+
for (const entry of entries) {
|
|
50
|
+
if (entry.startsWith('.') || entry.startsWith('_') || entry === 'node_modules')
|
|
51
|
+
continue;
|
|
52
|
+
const full = join(dir, entry);
|
|
53
|
+
let isDir = false;
|
|
54
|
+
try {
|
|
55
|
+
isDir = statSync(full).isDirectory();
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
if (!isDir)
|
|
61
|
+
continue;
|
|
62
|
+
walk(full, `${routePath}/${entry}`, ownLayouts, out);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=routes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/runtime/routes.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,EAAE;AACF,8CAA8C;AAC9C,oCAAoC;AACpC,+DAA+D;AAC/D,yCAAyC;AACzC,0EAA0E;AAC1E,uEAAuE;AACvE,EAAE;AACF,qEAAqE;AACrE,mEAAmE;AAEnE,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AAWhC,MAAM,UAAU,cAAc,CAAC,MAAc;IAC3C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IAClC,MAAM,GAAG,GAAY,EAAE,CAAA;IACvB,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,CAAA;IACzB,oDAAoD;IACpD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAChB,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG;YAAE,OAAO,CAAC,CAAC,CAAA;QAC7B,IAAI,CAAC,CAAC,IAAI,KAAK,GAAG;YAAE,OAAO,CAAC,CAAA;QAC5B,OAAO,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACrC,CAAC,CAAC,CAAA;IACF,OAAO,GAAG,CAAA;AACZ,CAAC;AAED,SAAS,IAAI,CAAC,GAAW,EAAE,SAAiB,EAAE,OAAiB,EAAE,GAAY;IAC3E,IAAI,OAAiB,CAAA;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,CAAA;IAC5B,CAAC;IAAC,MAAM,CAAC;QACP,OAAM;IACR,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAA;IAChD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;IAE5C,0EAA0E;IAC1E,MAAM,UAAU,GAAG,SAAS,CAAC,CAAC,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IAE9E,IAAI,OAAO,EAAE,CAAC;QACZ,GAAG,CAAC,IAAI,CAAC;YACP,IAAI,EAAE,SAAS,IAAI,GAAG;YACtB,QAAQ,EAAE,IAAI,CAAC,GAAG,EAAE,UAAU,CAAC;YAC/B,WAAW,EAAE,UAAU;SACxB,CAAC,CAAA;IACJ,CAAC;IAED,gFAAgF;IAChF,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,KAAK,cAAc;YAAE,SAAQ;QACxF,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,CAAA;QAC7B,IAAI,KAAK,GAAG,KAAK,CAAA;QACjB,IAAI,CAAC;YACH,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,SAAQ;QACV,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,SAAQ;QACpB,IAAI,CAAC,IAAI,EAAE,GAAG,SAAS,IAAI,KAAK,EAAE,EAAE,UAAU,EAAE,GAAG,CAAC,CAAA;IACtD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type ShortcutHandlers = {
|
|
2
|
+
onOpen: () => void;
|
|
3
|
+
onRestart: () => void;
|
|
4
|
+
onQuit: () => void;
|
|
5
|
+
};
|
|
6
|
+
export declare function setupShortcuts(handlers: ShortcutHandlers): void;
|
|
7
|
+
export declare function teardownStdin(): void;
|
|
8
|
+
//# sourceMappingURL=shortcuts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shortcuts.d.ts","sourceRoot":"","sources":["../../src/runtime/shortcuts.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,gBAAgB,GAAG;IAC7B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,MAAM,EAAE,MAAM,IAAI,CAAA;CACnB,CAAA;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAW/D;AAED,wBAAgB,aAAa,IAAI,IAAI,CAIpC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Terminal keyboard shortcuts (raw mode stdin).
|
|
2
|
+
//
|
|
3
|
+
// o open the window
|
|
4
|
+
// r restart (close + open)
|
|
5
|
+
// q quit
|
|
6
|
+
// Ctrl+C quit
|
|
7
|
+
export function setupShortcuts(handlers) {
|
|
8
|
+
if (!process.stdin.isTTY)
|
|
9
|
+
return;
|
|
10
|
+
process.stdin.setRawMode(true);
|
|
11
|
+
process.stdin.resume();
|
|
12
|
+
process.stdin.setEncoding('utf8');
|
|
13
|
+
process.stdin.on('data', (key) => {
|
|
14
|
+
const k = key.toString();
|
|
15
|
+
if (k === 'o' || k === 'O')
|
|
16
|
+
handlers.onOpen();
|
|
17
|
+
else if (k === 'r' || k === 'R')
|
|
18
|
+
handlers.onRestart();
|
|
19
|
+
else if (k === 'q' || k === 'Q' || k === '\x03' /* Ctrl+C */)
|
|
20
|
+
handlers.onQuit();
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
export function teardownStdin() {
|
|
24
|
+
try {
|
|
25
|
+
process.stdin.setRawMode(false);
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=shortcuts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shortcuts.js","sourceRoot":"","sources":["../../src/runtime/shortcuts.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,wBAAwB;AACxB,+BAA+B;AAC/B,aAAa;AACb,kBAAkB;AAQlB,MAAM,UAAU,cAAc,CAAC,QAA0B;IACvD,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAM;IAChC,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,CAAA;IAC9B,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAA;IACtB,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,CAAA;IACjC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE;QAC/B,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAA;QACxB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;aACxC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG;YAAE,QAAQ,CAAC,SAAS,EAAE,CAAA;aAChD,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,MAAM,CAAC,YAAY;YAAE,QAAQ,CAAC,MAAM,EAAE,CAAA;IACjF,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,IAAI,CAAC;QACH,OAAO,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC,CAAA,CAAC;AACZ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Application } from '@webviewjs/webview';
|
|
2
|
+
import type { WindowConfig } from '../types.ts';
|
|
3
|
+
export declare const app: Application;
|
|
4
|
+
export declare function getConfig(): Readonly<WindowConfig>;
|
|
5
|
+
export declare function openWindow(): Promise<void>;
|
|
6
|
+
export declare function closeWindow(): void;
|
|
7
|
+
export declare function reloadWindow(triggerFile: string): Promise<void>;
|
|
8
|
+
export declare function runApp(): void;
|
|
9
|
+
export declare function exitApp(): void;
|
|
10
|
+
//# sourceMappingURL=window.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.d.ts","sourceRoot":"","sources":["../../src/runtime/window.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,WAAW,EAAe,MAAM,oBAAoB,CAAA;AAI7D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAS/C,eAAO,MAAM,GAAG,aAAqD,CAAA;AA+BrE,wBAAgB,SAAS,IAAI,QAAQ,CAAC,YAAY,CAAC,CAElD;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAahD;AAED,wBAAgB,WAAW,IAAI,IAAI,CAOlC;AAED,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CASrE;AAED,wBAAgB,MAAM,IAAI,IAAI,CAE7B;AAED,wBAAgB,OAAO,IAAI,IAAI,CAI9B"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Owns the Application + BrowserWindow lifecycle.
|
|
2
|
+
//
|
|
3
|
+
// The EventLoop is created exactly once (winit/tao limitation). ControlFlow.Wait
|
|
4
|
+
// keeps the loop alive even when no windows are open, so the user can press
|
|
5
|
+
// `o` in the terminal to re-open the window.
|
|
6
|
+
import { Application, ControlFlow } from '@webviewjs/webview';
|
|
7
|
+
import { printClosed, printError, printHint, printReloaded } from "../cli/log.js";
|
|
8
|
+
import { DEFAULT_WIN_SIZE, DEFAULT_WIN_TITLE, projectRoot } from "../env.js";
|
|
9
|
+
import { renderApp } from "./render.js";
|
|
10
|
+
const config = {
|
|
11
|
+
title: DEFAULT_WIN_TITLE,
|
|
12
|
+
width: DEFAULT_WIN_SIZE.width,
|
|
13
|
+
height: DEFAULT_WIN_SIZE.height,
|
|
14
|
+
};
|
|
15
|
+
export const app = new Application({ controlFlow: ControlFlow.Wait });
|
|
16
|
+
let win = null;
|
|
17
|
+
let webview = null;
|
|
18
|
+
app.onEvent((event) => {
|
|
19
|
+
const kind = event && (event.kind || event.event);
|
|
20
|
+
if (kind === 'window-close-requested') {
|
|
21
|
+
// Dispose so the OS close completes โ without this the close button hangs.
|
|
22
|
+
if (win) {
|
|
23
|
+
try {
|
|
24
|
+
win.dispose();
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
}
|
|
28
|
+
win = null;
|
|
29
|
+
webview = null;
|
|
30
|
+
printClosed();
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
function applyMetadata(metadata) {
|
|
34
|
+
if (!metadata)
|
|
35
|
+
return;
|
|
36
|
+
if (metadata.window?.title)
|
|
37
|
+
config.title = metadata.window.title;
|
|
38
|
+
else if (metadata.title)
|
|
39
|
+
config.title = metadata.title;
|
|
40
|
+
if (metadata.window?.width)
|
|
41
|
+
config.width = metadata.window.width;
|
|
42
|
+
if (metadata.window?.height)
|
|
43
|
+
config.height = metadata.window.height;
|
|
44
|
+
}
|
|
45
|
+
export function getConfig() {
|
|
46
|
+
return config;
|
|
47
|
+
}
|
|
48
|
+
export async function openWindow() {
|
|
49
|
+
if (win) {
|
|
50
|
+
printHint('Window is already open');
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const { html, metadata } = await renderApp();
|
|
54
|
+
applyMetadata(metadata);
|
|
55
|
+
win = app.createBrowserWindow({
|
|
56
|
+
title: config.title,
|
|
57
|
+
width: config.width,
|
|
58
|
+
height: config.height,
|
|
59
|
+
});
|
|
60
|
+
webview = win.createWebview({ html });
|
|
61
|
+
}
|
|
62
|
+
export function closeWindow() {
|
|
63
|
+
if (!win)
|
|
64
|
+
return;
|
|
65
|
+
try {
|
|
66
|
+
win.dispose();
|
|
67
|
+
}
|
|
68
|
+
catch { }
|
|
69
|
+
win = null;
|
|
70
|
+
webview = null;
|
|
71
|
+
}
|
|
72
|
+
export async function reloadWindow(triggerFile) {
|
|
73
|
+
if (!webview)
|
|
74
|
+
return;
|
|
75
|
+
try {
|
|
76
|
+
const { html } = await renderApp();
|
|
77
|
+
webview.loadHtml(html);
|
|
78
|
+
printReloaded(triggerFile.replace(projectRoot + '/', ''));
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
printError(`Reload failed: ${e.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export function runApp() {
|
|
85
|
+
app.run();
|
|
86
|
+
}
|
|
87
|
+
export function exitApp() {
|
|
88
|
+
try {
|
|
89
|
+
app.exit();
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=window.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"window.js","sourceRoot":"","sources":["../../src/runtime/window.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { Metadata } from './index.ts';
|
|
2
|
+
import type { Component } from './jsx/types.ts';
|
|
3
|
+
export type AppComponent = Component;
|
|
4
|
+
export type LayoutModule = {
|
|
5
|
+
component: AppComponent;
|
|
6
|
+
metadata?: Metadata;
|
|
7
|
+
} | null;
|
|
8
|
+
export type WindowConfig = {
|
|
9
|
+
title: string;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
};
|
|
13
|
+
export type RenderResult = {
|
|
14
|
+
html: string;
|
|
15
|
+
metadata?: Metadata;
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C,MAAM,MAAM,YAAY,GAAG,SAAS,CAAA;AAEpC,MAAM,MAAM,YAAY,GAAG;IACzB,SAAS,EAAE,YAAY,CAAA;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,GAAG,IAAI,CAAA;AAER,MAAM,MAAM,YAAY,GAAG;IACzB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,CAAC,EAAE,QAAQ,CAAA;CACpB,CAAA"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,iFAAiF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "murasaki",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "The desktop framework for Next.js developers. Node-powered. WebView-thin. No Rust. No Chromium.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"desktop",
|
|
@@ -28,29 +28,35 @@
|
|
|
28
28
|
},
|
|
29
29
|
"exports": {
|
|
30
30
|
".": {
|
|
31
|
-
"types": "./
|
|
32
|
-
"default": "./
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
33
|
},
|
|
34
34
|
"./jsx": {
|
|
35
|
-
"types": "./
|
|
36
|
-
"default": "./
|
|
35
|
+
"types": "./dist/jsx/index.d.ts",
|
|
36
|
+
"default": "./dist/jsx/index.js"
|
|
37
37
|
},
|
|
38
38
|
"./jsx-runtime": {
|
|
39
|
-
"types": "./
|
|
40
|
-
"default": "./
|
|
39
|
+
"types": "./dist/jsx/jsx-runtime.d.ts",
|
|
40
|
+
"default": "./dist/jsx/jsx-runtime.js"
|
|
41
41
|
},
|
|
42
42
|
"./jsx-dev-runtime": {
|
|
43
|
-
"types": "./
|
|
44
|
-
"default": "./
|
|
43
|
+
"types": "./dist/jsx/jsx-dev-runtime.d.ts",
|
|
44
|
+
"default": "./dist/jsx/jsx-dev-runtime.js"
|
|
45
|
+
},
|
|
46
|
+
"./jsx/dom": {
|
|
47
|
+
"types": "./dist/jsx/dom/index.d.ts",
|
|
48
|
+
"default": "./dist/jsx/dom/index.js"
|
|
45
49
|
}
|
|
46
50
|
},
|
|
47
51
|
"files": [
|
|
48
52
|
"bin",
|
|
49
|
-
"
|
|
53
|
+
"dist",
|
|
50
54
|
"README.md",
|
|
51
55
|
"LICENSE"
|
|
52
56
|
],
|
|
53
57
|
"scripts": {
|
|
58
|
+
"build": "tsc -p tsconfig.build.json",
|
|
59
|
+
"prepublishOnly": "pnpm run build",
|
|
54
60
|
"example:app-router": "cd examples/app-router && pnpm dev",
|
|
55
61
|
"check": "biome check",
|
|
56
62
|
"lint": "biome lint",
|
|
@@ -59,6 +65,7 @@
|
|
|
59
65
|
},
|
|
60
66
|
"dependencies": {
|
|
61
67
|
"@webviewjs/webview": "^0.3.2",
|
|
68
|
+
"esbuild": "^0.28.1",
|
|
62
69
|
"tsx": "^4.22.4"
|
|
63
70
|
},
|
|
64
71
|
"devDependencies": {
|
package/src/cli/colors.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
// ANSI truecolor palette (Oomurasaki) + helper.
|
|
2
|
-
|
|
3
|
-
export const BRIGHT = '\x1b[38;2;168;85;247m'
|
|
4
|
-
export const DEEP = '\x1b[38;2;91;33;182m'
|
|
5
|
-
export const CREAM = '\x1b[38;2;250;245;232m'
|
|
6
|
-
export const DARK = '\x1b[38;2;59;7;100m'
|
|
7
|
-
export const DIM = '\x1b[38;2;136;136;153m'
|
|
8
|
-
export const GREEN = '\x1b[38;2;76;175;80m'
|
|
9
|
-
export const RED = '\x1b[38;2;239;68;68m'
|
|
10
|
-
export const BOLD = '\x1b[1m'
|
|
11
|
-
export const RESET = '\x1b[0m'
|
|
12
|
-
|
|
13
|
-
export const noColor = Boolean(process.env.NO_COLOR) || !process.stdout.isTTY
|
|
14
|
-
|
|
15
|
-
/** Wrap an ANSI escape; returns '' if colors are disabled. */
|
|
16
|
-
export const c = (code: string): string => (noColor ? '' : code)
|
package/src/cli/log.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
// Banner + status output for the dev runner.
|
|
2
|
-
|
|
3
|
-
import { projectRoot, VERSION, WEBVIEW_ENGINE } from '../env.ts'
|
|
4
|
-
import { BOLD, BRIGHT, c, DIM, GREEN, RED, RESET } from './colors.ts'
|
|
5
|
-
|
|
6
|
-
const out = (s: string) => process.stdout.write(s)
|
|
7
|
-
|
|
8
|
-
export function printBanner(winTitle: string, winSize: { width: number; height: number }) {
|
|
9
|
-
out('\n')
|
|
10
|
-
out(` ${c(BOLD)}${c(BRIGHT)}๐ฆ Murasaki${c(RESET)} ${c(DIM)}${VERSION}${c(RESET)}\n\n`)
|
|
11
|
-
out(` ${c(DIM)}-${c(RESET)} ${c(DIM)}Project ${c(RESET)}${projectRoot}\n`)
|
|
12
|
-
out(
|
|
13
|
-
` ${c(DIM)}-${c(RESET)} ${c(DIM)}Window ${c(RESET)}${winTitle} ${c(DIM)}(${winSize.width}ร${winSize.height})${c(RESET)}\n`,
|
|
14
|
-
)
|
|
15
|
-
out(` ${c(DIM)}-${c(RESET)} ${c(DIM)}Webview ${c(RESET)}${WEBVIEW_ENGINE}\n`)
|
|
16
|
-
out(` ${c(DIM)}-${c(RESET)} ${c(DIM)}Runtime ${c(RESET)}Node ${process.version}\n`)
|
|
17
|
-
out(
|
|
18
|
-
` ${c(DIM)}-${c(RESET)} ${c(DIM)}Mode ${c(RESET)}development ${c(DIM)}(HMR active)${c(RESET)}\n\n`,
|
|
19
|
-
)
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export function printShortcuts() {
|
|
23
|
-
out(
|
|
24
|
-
` ${c(DIM)}Shortcuts ${c(RESET)}${c(BOLD)}o${c(RESET)} ${c(DIM)}open${c(RESET)} ${c(BOLD)}r${c(RESET)} ${c(DIM)}restart${c(RESET)} ${c(BOLD)}q${c(RESET)} ${c(DIM)}quit${c(RESET)}\n\n`,
|
|
25
|
-
)
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export const printStarting = () => out(` ${c(DIM)}โ${c(RESET)} Starting...\n`)
|
|
29
|
-
export const printReady = (ms: number) =>
|
|
30
|
-
out(
|
|
31
|
-
` ${c(GREEN)}${c(BOLD)}โ${c(RESET)} ${c(BOLD)}Ready${c(RESET)} ${c(DIM)}in ${ms}ms${c(RESET)}\n`,
|
|
32
|
-
)
|
|
33
|
-
export const printOpened = () => out(` ${c(GREEN)}${c(BOLD)}โ${c(RESET)} Window opened\n`)
|
|
34
|
-
export const printClosed = () =>
|
|
35
|
-
out(
|
|
36
|
-
` ${c(DIM)}โ${c(RESET)} Window closed ${c(DIM)}โ press ${c(RESET)}${c(BOLD)}o${c(RESET)}${c(DIM)} to re-open, ${c(RESET)}${c(BOLD)}q${c(RESET)}${c(DIM)} to quit${c(RESET)}\n`,
|
|
37
|
-
)
|
|
38
|
-
export const printReloaded = (file: string) =>
|
|
39
|
-
out(` ${c(BRIGHT)}${c(BOLD)}โป${c(RESET)} Reloaded ${c(DIM)}${file}${c(RESET)}\n`)
|
|
40
|
-
export const printBye = () => out(`\n ${c(DIM)}Bye ๐ฆ${c(RESET)}\n\n`)
|
|
41
|
-
export const printHint = (msg: string) => out(` ${c(DIM)}ยท ${msg}${c(RESET)}\n`)
|
|
42
|
-
export const printError = (msg: string) => out(` ${c(RED)}${c(BOLD)}โ${c(RESET)} ${msg}\n`)
|