tina4-nodejs 3.13.148 → 3.13.150
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/CLAUDE.md +4 -3
- package/package.json +1 -1
- package/packages/cli/dist/bin.js +10898 -9844
- package/packages/cli/src/commands/generate.ts +184 -25
- package/packages/core/dist/index.js +10864 -9810
- package/packages/core/public/bundled-component-provenance.json +2 -2
- package/packages/core/public/js/tina4-dev-admin-split.html +202 -0
- package/packages/core/public/js/tina4-dev-admin.min.js +147 -138
- package/packages/core/src/devAdmin.ts +105 -1
- package/packages/core/src/index.ts +3 -0
- package/packages/core/src/messenger.ts +57 -1
- package/packages/core/src/metrics.ts +5 -1
- package/packages/core/src/plan.ts +1 -1
- package/packages/core/src/router.ts +11 -1
- package/packages/core/src/server.ts +5 -2
- package/packages/core/templates/crud/form.twig +26 -0
- package/packages/core/templates/crud/modals.twig +31 -0
- package/packages/core/templates/crud/page.twig +401 -0
- package/packages/core/templates/crud/table.twig +69 -0
- package/packages/frond/dist/index.js +9 -1
- package/packages/frond/src/engine.ts +9 -0
- package/packages/frond/src/index.ts +1 -1
- package/packages/orm/dist/index.js +1283 -219
- package/packages/orm/src/adapters/mongodb.ts +209 -34
- package/packages/orm/src/autoCrud.ts +9 -0
- package/packages/orm/src/crud.ts +273 -0
- package/packages/orm/src/crudHelpers.ts +194 -0
- package/packages/orm/src/crudRender.ts +202 -0
- package/packages/orm/src/crudTable.ts +171 -0
- package/packages/orm/src/index.ts +7 -1
- package/packages/orm/src/migration.ts +167 -4
- package/packages/orm/src/query.ts +99 -40
- package/packages/orm/src/sqlTranslator.ts +8 -1
- package/packages/orm/src/types.ts +19 -0
- package/types/core/src/devAdmin.d.ts +23 -0
- package/types/core/src/messenger.d.ts +4 -0
- package/types/core/src/router.d.ts +0 -5
- package/types/frond/src/engine.d.ts +6 -0
- package/types/frond/src/index.d.ts +1 -1
- package/types/orm/src/crud.d.ts +82 -0
- package/types/orm/src/crudHelpers.d.ts +63 -0
- package/types/orm/src/crudRender.d.ts +38 -0
- package/types/orm/src/crudTable.d.ts +45 -0
- package/types/orm/src/index.d.ts +3 -1
- package/types/orm/src/migration.d.ts +0 -18
- package/types/orm/src/query.d.ts +0 -12
- package/types/orm/src/types.d.ts +19 -0
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
},
|
|
42
42
|
{
|
|
43
43
|
"path": "js/tina4-dev-admin.min.js",
|
|
44
|
-
"component": "tina4-dev-admin-
|
|
45
|
-
"sha256": "
|
|
44
|
+
"component": "tina4-dev-admin-8c4617d",
|
|
45
|
+
"sha256": "6b05be06b5d76ee4126a11f019d104d3920c1e3234250cfe1966a33f34c1a234"
|
|
46
46
|
}
|
|
47
47
|
]
|
|
48
48
|
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0"><title>Tina4 Dev - Split</title>
|
|
3
|
+
<style>
|
|
4
|
+
html,body{margin:0;height:100%;background:#0f172a;font-family:system-ui,-apple-system,sans-serif;overflow:hidden}
|
|
5
|
+
/* One top toolbar controls both panels and the preview device. */
|
|
6
|
+
.t4-topbar{height:40px;box-sizing:border-box;display:flex;align-items:center;gap:6px;padding:0 10px;background:#0b1220;border-bottom:1px solid #1f2937}
|
|
7
|
+
.t4-brand{color:#e2e8f0;font:600 12px system-ui;margin-right:6px}
|
|
8
|
+
.t4-spacer{flex:1 1 auto}
|
|
9
|
+
.t4-devbtn{background:#1e293b;color:#cbd5e1;border:1px solid #334155;border-radius:6px;padding:3px 9px;font:12px system-ui;cursor:pointer}
|
|
10
|
+
.t4-devbtn.active{background:#2563eb;border-color:#2563eb;color:#fff}
|
|
11
|
+
/* Icon toggle buttons: full opacity + highlight when the panel is shown,
|
|
12
|
+
dimmed when hidden. */
|
|
13
|
+
.t4-tg{display:inline-flex;align-items:center;justify-content:center;width:28px;height:26px;background:#1e293b;color:#94a3b8;border:1px solid #334155;border-radius:6px;cursor:pointer;opacity:.5}
|
|
14
|
+
.t4-tg svg{width:15px;height:15px}
|
|
15
|
+
.t4-tg.active{background:#334155;color:#fff;opacity:1}
|
|
16
|
+
.t4-dim{color:#64748b;font:11px/1 ui-monospace,monospace;margin:0 6px}
|
|
17
|
+
.t4-popout{background:#0f766e;color:#fff;border:1px solid #0f766e;border-radius:6px;padding:3px 10px;font:12px system-ui;cursor:pointer;margin-left:6px}
|
|
18
|
+
.t4-split{display:flex;height:calc(100vh - 41px);width:100vw}
|
|
19
|
+
.t4-left{flex:0 0 auto;width:58%;min-width:0;height:100%;overflow:hidden}
|
|
20
|
+
.t4-left iframe{width:100%;height:100%;border:0;display:block}
|
|
21
|
+
.t4-gutter{flex:0 0 6px;cursor:col-resize;background:#1f2937}
|
|
22
|
+
.t4-gutter:hover,.t4-gutter.drag{background:#2563eb}
|
|
23
|
+
.t4-right{flex:1 1 auto;min-width:0;height:100%;overflow:hidden;display:flex;flex-direction:column}
|
|
24
|
+
/* A LIGHT backdrop so the dark device frame stands out. */
|
|
25
|
+
.t4-stage{flex:1 1 auto;min-width:0;min-height:0;position:relative;overflow:hidden;display:flex;align-items:center;justify-content:center;background:radial-gradient(circle at 50% 32%,#f8fafc,#c3ccd9)}
|
|
26
|
+
/* The device frame - a phone/tablet bezel around the live app. overflow:hidden
|
|
27
|
+
+ border-radius CLIP the app to the rounded frame, so content is contained.
|
|
28
|
+
No width/height transition: fit() measures right after a toggle, and an
|
|
29
|
+
animated size would be read mid-transition (the "clicking does nothing" bug). */
|
|
30
|
+
.t4-device{position:relative;background:#111;box-shadow:0 16px 50px rgba(15,23,42,.45);transform-origin:center center;overflow:hidden}
|
|
31
|
+
.t4-device iframe{width:100%;height:100%;border:0;display:block;background:#fff}
|
|
32
|
+
.t4-device.mobile{border:14px solid #111;border-radius:46px}
|
|
33
|
+
.t4-device.tablet{border:16px solid #111;border-radius:26px}
|
|
34
|
+
/* Desktop fills the whole preview block - no bezel, no scaling. */
|
|
35
|
+
.t4-device.desktop{border:0;border-radius:0;box-shadow:none;width:100%;height:100%}
|
|
36
|
+
.t4-hidden{display:none !important}
|
|
37
|
+
/* While dragging the gutter, let mouse events pass over the iframes. */
|
|
38
|
+
body.t4-dragging iframe{pointer-events:none}
|
|
39
|
+
</style></head>
|
|
40
|
+
<body>
|
|
41
|
+
<div class="t4-topbar">
|
|
42
|
+
<span class="t4-brand">Tina4 Dev</span>
|
|
43
|
+
<button class="t4-tg active" id="t4-tg-files" title="Show/hide the file navigator"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4"><rect x="1.5" y="2.5" width="13" height="11" rx="1.5"/><line x1="6" y1="2.5" x2="6" y2="13.5"/></svg></button>
|
|
44
|
+
<button class="t4-tg active" id="t4-tg-editor" title="Show/hide the code editor"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="5.5,4.5 2,8 5.5,11.5"/><polyline points="10.5,4.5 14,8 10.5,11.5"/></svg></button>
|
|
45
|
+
<button class="t4-tg active" id="t4-tg-preview" title="Show/hide the preview"><svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"><rect x="1.5" y="2.5" width="13" height="9" rx="1.5"/><line x1="5.5" y1="14" x2="10.5" y2="14"/><line x1="8" y1="11.5" x2="8" y2="14"/></svg></button>
|
|
46
|
+
<span class="t4-spacer"></span>
|
|
47
|
+
<span class="t4-dim" id="t4-dim">1440 x 900</span>
|
|
48
|
+
<button class="t4-devbtn" data-mode="mobile">Mobile</button>
|
|
49
|
+
<button class="t4-devbtn" data-mode="tablet">Tablet</button>
|
|
50
|
+
<button class="t4-devbtn active" data-mode="desktop">Desktop</button>
|
|
51
|
+
<button class="t4-popout" id="t4-popout" title="Open this view in its own window - drag it to another screen">⇱ Pop out</button>
|
|
52
|
+
</div>
|
|
53
|
+
<div class="t4-split" id="t4-split">
|
|
54
|
+
<div class="t4-left" id="t4-left"><iframe src="/__dev" title="Tina4 Dev Admin"></iframe></div>
|
|
55
|
+
<div class="t4-gutter" id="t4-gutter"></div>
|
|
56
|
+
<div class="t4-right" id="t4-right">
|
|
57
|
+
<div class="t4-stage" id="t4-stage">
|
|
58
|
+
<div class="t4-device desktop" id="t4-device">
|
|
59
|
+
<iframe id="t4-preview" src="/" title="Live preview"></iframe>
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</div>
|
|
63
|
+
</div>
|
|
64
|
+
<script>
|
|
65
|
+
(function () {
|
|
66
|
+
var SIZES = { mobile: {w:390,h:844}, tablet: {w:820,h:1180}, desktop: {w:1440,h:900} };
|
|
67
|
+
var KEY = 'tina4_split_device';
|
|
68
|
+
var device = document.getElementById('t4-device');
|
|
69
|
+
var stage = document.getElementById('t4-stage');
|
|
70
|
+
var dim = document.getElementById('t4-dim');
|
|
71
|
+
var left = document.getElementById('t4-left');
|
|
72
|
+
var right = document.getElementById('t4-right');
|
|
73
|
+
var gutter = document.getElementById('t4-gutter');
|
|
74
|
+
var split = document.getElementById('t4-split');
|
|
75
|
+
var mode = 'desktop';
|
|
76
|
+
try { var saved = localStorage.getItem(KEY); if (saved && SIZES[saved]) mode = saved; } catch (_) {}
|
|
77
|
+
var leftWidth = '58%';
|
|
78
|
+
|
|
79
|
+
// ── device preview ──
|
|
80
|
+
function fit() {
|
|
81
|
+
if (right.classList.contains('t4-hidden')) return;
|
|
82
|
+
if (mode === 'desktop') {
|
|
83
|
+
// Desktop just fills the preview block - responsive, no frame, no scale.
|
|
84
|
+
device.style.transform = 'none';
|
|
85
|
+
device.style.width = '100%';
|
|
86
|
+
device.style.height = '100%';
|
|
87
|
+
var r0 = stage.getBoundingClientRect();
|
|
88
|
+
if (dim) dim.textContent = Math.round(r0.width) + ' x ' + Math.round(r0.height);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
var s = SIZES[mode];
|
|
92
|
+
device.style.transform = 'none';
|
|
93
|
+
device.style.width = s.w + 'px';
|
|
94
|
+
device.style.height = s.h + 'px';
|
|
95
|
+
var rect = stage.getBoundingClientRect();
|
|
96
|
+
var pad = 28;
|
|
97
|
+
var ow = device.offsetWidth, oh = device.offsetHeight;
|
|
98
|
+
var scale = Math.min((rect.width - pad) / ow, (rect.height - pad) / oh, 1);
|
|
99
|
+
if (!isFinite(scale) || scale <= 0) scale = 1;
|
|
100
|
+
device.style.transform = 'scale(' + scale + ')';
|
|
101
|
+
}
|
|
102
|
+
function setMode(m) {
|
|
103
|
+
if (!SIZES[m]) return;
|
|
104
|
+
mode = m;
|
|
105
|
+
device.className = 't4-device ' + m;
|
|
106
|
+
if (dim) dim.textContent = SIZES[m].w + ' x ' + SIZES[m].h;
|
|
107
|
+
document.querySelectorAll('.t4-devbtn').forEach(function (b) {
|
|
108
|
+
b.classList.toggle('active', b.getAttribute('data-mode') === m);
|
|
109
|
+
});
|
|
110
|
+
try { localStorage.setItem(KEY, m); } catch (_) {}
|
|
111
|
+
fit();
|
|
112
|
+
}
|
|
113
|
+
document.querySelectorAll('.t4-devbtn').forEach(function (b) {
|
|
114
|
+
b.addEventListener('click', function () { setMode(b.getAttribute('data-mode')); });
|
|
115
|
+
});
|
|
116
|
+
var pop = document.getElementById('t4-popout');
|
|
117
|
+
if (pop) pop.addEventListener('click', function () {
|
|
118
|
+
var s = SIZES[mode];
|
|
119
|
+
var w = Math.min(s.w, (screen.availWidth || 1920) - 40);
|
|
120
|
+
var h = Math.min(s.h, (screen.availHeight || 1080) - 120);
|
|
121
|
+
window.open('/', 'tina4preview_' + mode, 'width=' + w + ',height=' + h + ',menubar=no,toolbar=no,location=no');
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// ── show / hide each panel (Files, Editor, Preview) ──
|
|
125
|
+
// Files (the nav) and Editor (the code area) are SEPARATE sections living
|
|
126
|
+
// inside the left /__dev SPA iframe, so hiding the editor must not take the
|
|
127
|
+
// file nav with it. They are same-origin, so we reach into the iframe and
|
|
128
|
+
// toggle its own `#editor-sidebar` (nav) and `.editor-main` (editor). The
|
|
129
|
+
// Preview is the right pane of this page.
|
|
130
|
+
var vis = { files: true, editor: true, preview: true };
|
|
131
|
+
function frameDoc() {
|
|
132
|
+
try { return document.querySelector('#t4-left iframe').contentDocument; } catch (_) { return null; }
|
|
133
|
+
}
|
|
134
|
+
function setFrameEl(sel, show) {
|
|
135
|
+
var d = frameDoc(); if (!d) return;
|
|
136
|
+
var el = d.querySelector(sel); if (el) el.style.display = show ? '' : 'none';
|
|
137
|
+
}
|
|
138
|
+
function applyPanels() {
|
|
139
|
+
setFrameEl('#editor-sidebar', vis.files);
|
|
140
|
+
setFrameEl('#editor-splitter-left', vis.files && vis.editor);
|
|
141
|
+
setFrameEl('.editor-main', vis.editor);
|
|
142
|
+
var leftEmpty = !vis.files && !vis.editor;
|
|
143
|
+
left.classList.toggle('t4-hidden', leftEmpty);
|
|
144
|
+
right.classList.toggle('t4-hidden', !vis.preview);
|
|
145
|
+
gutter.classList.toggle('t4-hidden', leftEmpty || !vis.preview);
|
|
146
|
+
if (!vis.preview) {
|
|
147
|
+
// Preview hidden: the editor/nav fills the whole width.
|
|
148
|
+
left.style.flex = '1 1 auto'; left.style.width = 'auto';
|
|
149
|
+
} else if (vis.files && !vis.editor) {
|
|
150
|
+
// Only the file nav is shown: shrink the left pane to the nav width so
|
|
151
|
+
// there is no empty gap where the editor used to be.
|
|
152
|
+
left.style.flex = '0 0 auto'; left.style.width = '250px';
|
|
153
|
+
} else {
|
|
154
|
+
left.style.flex = '0 0 auto'; left.style.width = leftWidth;
|
|
155
|
+
}
|
|
156
|
+
document.getElementById('t4-tg-files').classList.toggle('active', vis.files);
|
|
157
|
+
document.getElementById('t4-tg-editor').classList.toggle('active', vis.editor);
|
|
158
|
+
document.getElementById('t4-tg-preview').classList.toggle('active', vis.preview);
|
|
159
|
+
fit();
|
|
160
|
+
}
|
|
161
|
+
function toggle(name) {
|
|
162
|
+
// Never hide all three - at least one stays visible.
|
|
163
|
+
var n = { files: vis.files, editor: vis.editor, preview: vis.preview };
|
|
164
|
+
n[name] = !n[name];
|
|
165
|
+
if (!n.files && !n.editor && !n.preview) return;
|
|
166
|
+
vis[name] = !vis[name];
|
|
167
|
+
applyPanels();
|
|
168
|
+
}
|
|
169
|
+
document.getElementById('t4-tg-files').addEventListener('click', function () { toggle('files'); });
|
|
170
|
+
document.getElementById('t4-tg-editor').addEventListener('click', function () { toggle('editor'); });
|
|
171
|
+
document.getElementById('t4-tg-preview').addEventListener('click', function () { toggle('preview'); });
|
|
172
|
+
|
|
173
|
+
// ── drag the gutter to resize ──
|
|
174
|
+
var dragging = false;
|
|
175
|
+
gutter.addEventListener('mousedown', function (e) {
|
|
176
|
+
dragging = true;
|
|
177
|
+
document.body.classList.add('t4-dragging');
|
|
178
|
+
gutter.classList.add('drag');
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
});
|
|
181
|
+
window.addEventListener('mousemove', function (e) {
|
|
182
|
+
if (!dragging) return;
|
|
183
|
+
var r = split.getBoundingClientRect();
|
|
184
|
+
var w = e.clientX - r.left;
|
|
185
|
+
w = Math.max(220, Math.min(w, r.width - 260));
|
|
186
|
+
leftWidth = w + 'px';
|
|
187
|
+
left.style.width = leftWidth;
|
|
188
|
+
fit();
|
|
189
|
+
});
|
|
190
|
+
window.addEventListener('mouseup', function () {
|
|
191
|
+
if (!dragging) return;
|
|
192
|
+
dragging = false;
|
|
193
|
+
document.body.classList.remove('t4-dragging');
|
|
194
|
+
gutter.classList.remove('drag');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
window.addEventListener('resize', fit);
|
|
198
|
+
setMode(mode);
|
|
199
|
+
applyPanels();
|
|
200
|
+
})();
|
|
201
|
+
</script>
|
|
202
|
+
</body></html>
|