nova64 0.2.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.
Files changed (52) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +786 -0
  3. package/index.html +651 -0
  4. package/package.json +255 -0
  5. package/public/os9-shell/assets/index-B1Uvacma.js +32825 -0
  6. package/public/os9-shell/assets/index-B1Uvacma.js.map +1 -0
  7. package/public/os9-shell/assets/index-DIHfrTaW.css +1 -0
  8. package/public/os9-shell/index.html +14 -0
  9. package/public/os9-shell/nova-icon.svg +12 -0
  10. package/runtime/api-2d.js +878 -0
  11. package/runtime/api-3d/camera.js +73 -0
  12. package/runtime/api-3d/instancing.js +180 -0
  13. package/runtime/api-3d/lights.js +51 -0
  14. package/runtime/api-3d/materials.js +47 -0
  15. package/runtime/api-3d/models.js +84 -0
  16. package/runtime/api-3d/pbr.js +69 -0
  17. package/runtime/api-3d/primitives.js +304 -0
  18. package/runtime/api-3d/scene.js +169 -0
  19. package/runtime/api-3d/transforms.js +161 -0
  20. package/runtime/api-3d.js +154 -0
  21. package/runtime/api-effects.js +753 -0
  22. package/runtime/api-presets.js +85 -0
  23. package/runtime/api-skybox.js +178 -0
  24. package/runtime/api-sprites.js +100 -0
  25. package/runtime/api-voxel.js +601 -0
  26. package/runtime/api.js +201 -0
  27. package/runtime/assets.js +27 -0
  28. package/runtime/audio.js +114 -0
  29. package/runtime/collision.js +47 -0
  30. package/runtime/console.js +101 -0
  31. package/runtime/editor.js +233 -0
  32. package/runtime/font.js +233 -0
  33. package/runtime/framebuffer.js +28 -0
  34. package/runtime/fullscreen-button.js +185 -0
  35. package/runtime/gpu-canvas2d.js +47 -0
  36. package/runtime/gpu-threejs.js +639 -0
  37. package/runtime/gpu-webgl2.js +310 -0
  38. package/runtime/index.js +22 -0
  39. package/runtime/input.js +225 -0
  40. package/runtime/logger.js +60 -0
  41. package/runtime/physics.js +101 -0
  42. package/runtime/screens.js +213 -0
  43. package/runtime/storage.js +38 -0
  44. package/runtime/store.js +151 -0
  45. package/runtime/textinput.js +68 -0
  46. package/runtime/ui/buttons.js +124 -0
  47. package/runtime/ui/panels.js +105 -0
  48. package/runtime/ui/text.js +86 -0
  49. package/runtime/ui/widgets.js +141 -0
  50. package/runtime/ui.js +111 -0
  51. package/src/main.js +474 -0
  52. package/vite.config.js +63 -0
@@ -0,0 +1,105 @@
1
+ // runtime/ui/panels.js
2
+ // Panel (dialog box) creation and rendering
3
+
4
+ import { unpackRGBA64 } from '../api.js';
5
+
6
+ /**
7
+ * @param {{ g: object, colors: object, panels: Array, state: object,
8
+ * drawText: Function, setTextAlign: Function }} ctx
9
+ */
10
+ export function uiPanelsModule({ g, colors, panels, state, drawText, setTextAlign }) {
11
+ function createPanel(x, y, width, height, options = {}) {
12
+ const panel = {
13
+ id: `panel_${Date.now()}_${Math.random()}`,
14
+ x,
15
+ y,
16
+ width,
17
+ height,
18
+ bgColor: options.bgColor || g.rgba8(0, 0, 0, 200),
19
+ borderColor: options.borderColor || colors.primary,
20
+ borderWidth: options.borderWidth !== undefined ? options.borderWidth : 2,
21
+ cornerRadius: options.cornerRadius || 0,
22
+ shadow: options.shadow || false,
23
+ shadowOffset: options.shadowOffset || 4,
24
+ title: options.title || null,
25
+ titleColor: options.titleColor || colors.white,
26
+ titleBgColor: options.titleBgColor || colors.primary,
27
+ padding: options.padding || 10,
28
+ visible: options.visible !== undefined ? options.visible : true,
29
+ gradient: options.gradient || false,
30
+ gradientColor: options.gradientColor || g.rgba8(0, 0, 50, 200),
31
+ };
32
+ panels.push(panel);
33
+ return panel;
34
+ }
35
+
36
+ function drawPanel(panel) {
37
+ if (!panel.visible) return;
38
+ const { x, y, width, height } = panel;
39
+
40
+ if (panel.shadow) {
41
+ const so = panel.shadowOffset;
42
+ g.rect(x + so, y + so, width, height, g.rgba8(0, 0, 0, 100), true);
43
+ }
44
+
45
+ if (panel.gradient) {
46
+ const steps = 20;
47
+ const bgU = unpackRGBA64(panel.bgColor);
48
+ const grU = unpackRGBA64(panel.gradientColor);
49
+ const bgR = Math.floor(bgU.r / 257),
50
+ bgG = Math.floor(bgU.g / 257),
51
+ bgB = Math.floor(bgU.b / 257),
52
+ bgA = Math.floor(bgU.a / 257);
53
+ const grR = Math.floor(grU.r / 257),
54
+ grG = Math.floor(grU.g / 257),
55
+ grB = Math.floor(grU.b / 257);
56
+ for (let i = 0; i < steps; i++) {
57
+ const ratio = i / steps;
58
+ const h = Math.floor(height / steps);
59
+ const r = Math.floor(bgR + (grR - bgR) * ratio);
60
+ const gv = Math.floor(bgG + (grG - bgG) * ratio);
61
+ const b = Math.floor(bgB + (grB - bgB) * ratio);
62
+ g.rect(x, y + i * h, width, h, g.rgba8(r, gv, b, bgA), true);
63
+ }
64
+ } else {
65
+ g.rect(x, y, width, height, panel.bgColor, true);
66
+ }
67
+
68
+ if (panel.cornerRadius > 0) {
69
+ const cr = panel.cornerRadius;
70
+ g.rect(x, y, cr, cr, panel.bgColor, true);
71
+ g.rect(x + width - cr, y, cr, cr, panel.bgColor, true);
72
+ g.rect(x, y + height - cr, cr, cr, panel.bgColor, true);
73
+ g.rect(x + width - cr, y + height - cr, cr, cr, panel.bgColor, true);
74
+ }
75
+
76
+ if (panel.borderWidth > 0) {
77
+ for (let i = 0; i < panel.borderWidth; i++) {
78
+ g.rect(x + i, y + i, width - i * 2, height - i * 2, panel.borderColor, false);
79
+ }
80
+ }
81
+
82
+ if (panel.title) {
83
+ const titleHeight = 20;
84
+ g.rect(x, y - titleHeight, width, titleHeight, panel.titleBgColor, true);
85
+ g.rect(x, y - titleHeight, width, titleHeight, panel.borderColor, false);
86
+ const oldAlign = state.textAlign;
87
+ setTextAlign('center');
88
+ drawText(panel.title, x + width / 2, y - titleHeight + 4, panel.titleColor, 1);
89
+ setTextAlign(oldAlign);
90
+ }
91
+ }
92
+
93
+ function drawAllPanels() {
94
+ panels.forEach(p => drawPanel(p));
95
+ }
96
+ function removePanel(p) {
97
+ const i = panels.indexOf(p);
98
+ if (i >= 0) panels.splice(i, 1);
99
+ }
100
+ function clearPanels() {
101
+ panels.length = 0;
102
+ }
103
+
104
+ return { createPanel, drawPanel, drawAllPanels, removePanel, clearPanels };
105
+ }
@@ -0,0 +1,86 @@
1
+ // runtime/ui/text.js
2
+ // Font management and text rendering
3
+
4
+ /**
5
+ * @param {{ g: object, fonts: object, state: { currentFont: string, textAlign: string, textBaseline: string }, colors: object }} ctx
6
+ */
7
+ export function uiTextModule({ g, fonts, state, colors }) {
8
+ function setFont(fontName) {
9
+ if (fonts[fontName]) state.currentFont = fontName;
10
+ }
11
+
12
+ function getFont() {
13
+ return fonts[state.currentFont];
14
+ }
15
+
16
+ function setTextAlign(align) {
17
+ state.textAlign = align;
18
+ }
19
+
20
+ function setTextBaseline(baseline) {
21
+ state.textBaseline = baseline;
22
+ }
23
+
24
+ function measureText(text, scale = 1) {
25
+ const font = getFont();
26
+ const charWidth = 6 * scale * font.size;
27
+ const charHeight = 8 * scale * font.size;
28
+ return { width: text.length * charWidth, height: charHeight };
29
+ }
30
+
31
+ function drawText(text, x, y, color = colors.white, scale = 1) {
32
+ const font = getFont();
33
+ const finalScale = scale * font.size;
34
+ const metrics = measureText(text, scale);
35
+
36
+ let drawX = x;
37
+ if (state.textAlign === 'center') drawX = x - metrics.width / 2;
38
+ else if (state.textAlign === 'right') drawX = x - metrics.width;
39
+
40
+ let drawY = y;
41
+ if (state.textBaseline === 'middle') drawY = y - metrics.height / 2;
42
+ else if (state.textBaseline === 'bottom') drawY = y - metrics.height;
43
+
44
+ g.print(text, Math.floor(drawX), Math.floor(drawY), color, finalScale);
45
+ }
46
+
47
+ function drawTextShadow(
48
+ text,
49
+ x,
50
+ y,
51
+ color = colors.white,
52
+ shadowColor = colors.black,
53
+ offset = 2,
54
+ scale = 1
55
+ ) {
56
+ drawText(text, x + offset, y + offset, shadowColor, scale);
57
+ drawText(text, x, y, color, scale);
58
+ }
59
+
60
+ function drawTextOutline(
61
+ text,
62
+ x,
63
+ y,
64
+ color = colors.white,
65
+ outlineColor = colors.black,
66
+ scale = 1
67
+ ) {
68
+ for (let ox = -1; ox <= 1; ox++) {
69
+ for (let oy = -1; oy <= 1; oy++) {
70
+ if (ox !== 0 || oy !== 0) drawText(text, x + ox, y + oy, outlineColor, scale);
71
+ }
72
+ }
73
+ drawText(text, x, y, color, scale);
74
+ }
75
+
76
+ return {
77
+ setFont,
78
+ getFont,
79
+ setTextAlign,
80
+ setTextBaseline,
81
+ measureText,
82
+ drawText,
83
+ drawTextShadow,
84
+ drawTextOutline,
85
+ };
86
+ }
@@ -0,0 +1,141 @@
1
+ // runtime/ui/widgets.js
2
+ // Progress bars, shapes, layout helpers, and mouse input
3
+
4
+ import { unpackRGBA64 } from '../api.js';
5
+
6
+ /**
7
+ * @param {{ g: object, colors: object, mouse: { x:number, y:number, down:boolean, pressed:boolean },
8
+ * state: object, drawText: Function, drawTextOutline: Function,
9
+ * setTextAlign: Function, setTextBaseline: Function }} ctx
10
+ */
11
+ export function uiWidgetsModule({
12
+ g,
13
+ colors,
14
+ mouse,
15
+ state,
16
+ drawTextOutline,
17
+ setTextAlign,
18
+ setTextBaseline,
19
+ }) {
20
+ // ── Progress bar ──────────────────────────────────────────────────────────
21
+ function drawProgressBar(x, y, width, height, value, maxValue, options = {}) {
22
+ const bgColor = options.bgColor || g.rgba8(50, 50, 50, 255);
23
+ const fillColor = options.fillColor || colors.success;
24
+ const borderColor = options.borderColor || colors.white;
25
+ const showText = options.showText !== undefined ? options.showText : true;
26
+ const textColor = options.textColor || colors.white;
27
+
28
+ g.rect(x, y, width, height, bgColor, true);
29
+ const fillWidth = Math.floor((value / maxValue) * width);
30
+ if (fillWidth > 0) g.rect(x, y, fillWidth, height, fillColor, true);
31
+ g.rect(x, y, width, height, borderColor, false);
32
+
33
+ if (showText) {
34
+ const text = `${Math.floor(value)}/${Math.floor(maxValue)}`;
35
+ const oldAlign = state.textAlign;
36
+ const oldBaseline = state.textBaseline;
37
+ setTextAlign('center');
38
+ setTextBaseline('middle');
39
+ drawTextOutline(text, x + width / 2, y + height / 2, textColor, colors.black, 1);
40
+ setTextAlign(oldAlign);
41
+ setTextBaseline(oldBaseline);
42
+ }
43
+ }
44
+
45
+ // ── Shapes ────────────────────────────────────────────────────────────────
46
+ function drawRoundedRect(x, y, width, height, radius, color, filled = true) {
47
+ if (radius === 0) {
48
+ g.rect(x, y, width, height, color, filled);
49
+ return;
50
+ }
51
+ g.rect(x + radius, y, width - radius * 2, height, color, filled);
52
+ g.rect(x, y + radius, radius, height - radius * 2, color, filled);
53
+ g.rect(x + width - radius, y + radius, radius, height - radius * 2, color, filled);
54
+ g.circle(x + radius, y + radius, radius, color, filled);
55
+ g.circle(x + width - radius, y + radius, radius, color, filled);
56
+ g.circle(x + radius, y + height - radius, radius, color, filled);
57
+ g.circle(x + width - radius, y + height - radius, radius, color, filled);
58
+ }
59
+
60
+ function drawGradientRect(x, y, width, height, color1, color2, vertical = true) {
61
+ const steps = vertical ? height : width;
62
+ const c1 = unpackRGBA64(color1);
63
+ const c2 = unpackRGBA64(color2);
64
+ const r1 = Math.floor(c1.r / 257),
65
+ g1 = Math.floor(c1.g / 257),
66
+ b1 = Math.floor(c1.b / 257),
67
+ a1 = Math.floor(c1.a / 257);
68
+ const r2 = Math.floor(c2.r / 257),
69
+ g2 = Math.floor(c2.g / 257),
70
+ b2 = Math.floor(c2.b / 257),
71
+ a2 = Math.floor(c2.a / 257);
72
+ for (let i = 0; i < steps; i++) {
73
+ const ratio = i / steps;
74
+ const r = Math.floor(r1 + (r2 - r1) * ratio);
75
+ const gv = Math.floor(g1 + (g2 - g1) * ratio);
76
+ const b = Math.floor(b1 + (b2 - b1) * ratio);
77
+ const a = Math.floor(a1 + (a2 - a1) * ratio);
78
+ const c = g.rgba8(r, gv, b, a);
79
+ if (vertical) g.rect(x, y + i, width, 1, c, true);
80
+ else g.rect(x + i, y, 1, height, c, true);
81
+ }
82
+ }
83
+
84
+ // ── Layout helpers ────────────────────────────────────────────────────────
85
+ function centerX(width, screenWidth = 640) {
86
+ return Math.floor((screenWidth - width) / 2);
87
+ }
88
+ function centerY(height, screenHeight = 360) {
89
+ return Math.floor((screenHeight - height) / 2);
90
+ }
91
+
92
+ function grid(cols, rows, cellWidth, cellHeight, paddingX = 0, paddingY = 0) {
93
+ const cells = [];
94
+ for (let row = 0; row < rows; row++) {
95
+ for (let col = 0; col < cols; col++) {
96
+ cells.push({
97
+ x: col * (cellWidth + paddingX),
98
+ y: row * (cellHeight + paddingY),
99
+ width: cellWidth,
100
+ height: cellHeight,
101
+ col,
102
+ row,
103
+ });
104
+ }
105
+ }
106
+ return cells;
107
+ }
108
+
109
+ // ── Mouse input ───────────────────────────────────────────────────────────
110
+ function setMousePosition(x, y) {
111
+ mouse.x = x;
112
+ mouse.y = y;
113
+ }
114
+ function setMouseButton(down) {
115
+ mouse.pressed = down && !mouse.down;
116
+ mouse.down = down;
117
+ }
118
+ function getMousePosition() {
119
+ return { x: mouse.x, y: mouse.y };
120
+ }
121
+ function isMouseDown() {
122
+ return mouse.down;
123
+ }
124
+ function isMousePressed() {
125
+ return mouse.pressed;
126
+ }
127
+
128
+ return {
129
+ drawProgressBar,
130
+ drawRoundedRect,
131
+ drawGradientRect,
132
+ centerX,
133
+ centerY,
134
+ grid,
135
+ setMousePosition,
136
+ setMouseButton,
137
+ getMousePosition,
138
+ isMouseDown,
139
+ isMousePressed,
140
+ };
141
+ }
package/runtime/ui.js ADDED
@@ -0,0 +1,111 @@
1
+ // 🎨 Nova64 UI System - First Class 2D Interface
2
+ // Thin orchestrator — delegates to runtime/ui/*.js sub-modules
3
+
4
+ import { uiTextModule } from './ui/text.js';
5
+ import { uiPanelsModule } from './ui/panels.js';
6
+ import { uiButtonsModule } from './ui/buttons.js';
7
+ import { uiWidgetsModule } from './ui/widgets.js';
8
+
9
+ export function uiApi(gpu, g) {
10
+ // Shared state objects passed by reference into sub-modules
11
+ const state = { currentFont: 'normal', textAlign: 'left', textBaseline: 'top' };
12
+ const mouse = { x: 0, y: 0, down: false, pressed: false };
13
+ const panels = [];
14
+ const buttons = [];
15
+
16
+ const fonts = {
17
+ tiny: { size: 1, spacing: 1 },
18
+ small: { size: 1, spacing: 2 },
19
+ normal: { size: 2, spacing: 1 },
20
+ large: { size: 3, spacing: 1 },
21
+ huge: { size: 4, spacing: 2 },
22
+ };
23
+
24
+ const rgba8 = g.rgba8;
25
+ const colors = {
26
+ primary: rgba8(0, 120, 255, 255),
27
+ secondary: rgba8(100, 100, 255, 255),
28
+ success: rgba8(0, 255, 100, 255),
29
+ warning: rgba8(255, 200, 0, 255),
30
+ danger: rgba8(255, 50, 50, 255),
31
+ dark: rgba8(20, 20, 30, 255),
32
+ light: rgba8(240, 240, 250, 255),
33
+ white: rgba8(255, 255, 255, 255),
34
+ black: rgba8(0, 0, 0, 255),
35
+ transparent: rgba8(0, 0, 0, 0),
36
+ };
37
+
38
+ const ctx = { g, fonts, state, mouse, colors, panels, buttons };
39
+
40
+ // Init modules — each appends its exports to ctx for later modules to use
41
+ const textMod = uiTextModule(ctx);
42
+ Object.assign(ctx, textMod);
43
+
44
+ const panelMod = uiPanelsModule(ctx);
45
+ Object.assign(ctx, panelMod);
46
+
47
+ const buttonMod = uiButtonsModule(ctx);
48
+ Object.assign(ctx, buttonMod);
49
+
50
+ const widgetMod = uiWidgetsModule(ctx);
51
+ Object.assign(ctx, widgetMod);
52
+
53
+ return {
54
+ // Expose these directly for main.js to wire input before exposeTo is called
55
+ setMousePosition: ctx.setMousePosition,
56
+ setMouseButton: ctx.setMouseButton,
57
+
58
+ exposeTo(target) {
59
+ Object.assign(target, {
60
+ // Font / text system
61
+ setFont: ctx.setFont,
62
+ getFont: ctx.getFont,
63
+ setTextAlign: ctx.setTextAlign,
64
+ setTextBaseline: ctx.setTextBaseline,
65
+ measureText: ctx.measureText,
66
+ drawText: ctx.drawText,
67
+ drawTextShadow: ctx.drawTextShadow,
68
+ drawTextOutline: ctx.drawTextOutline,
69
+
70
+ // Panel system
71
+ createPanel: ctx.createPanel,
72
+ drawPanel: ctx.drawPanel,
73
+ drawAllPanels: ctx.drawAllPanels,
74
+ removePanel: ctx.removePanel,
75
+ clearPanels: ctx.clearPanels,
76
+
77
+ // Button system
78
+ createButton: ctx.createButton,
79
+ updateButton: ctx.updateButton,
80
+ drawButton: ctx.drawButton,
81
+ updateAllButtons: ctx.updateAllButtons,
82
+ drawAllButtons: ctx.drawAllButtons,
83
+ removeButton: ctx.removeButton,
84
+ clearButtons: ctx.clearButtons,
85
+
86
+ // Progress bars
87
+ drawProgressBar: ctx.drawProgressBar,
88
+
89
+ // Advanced shapes
90
+ drawRoundedRect: ctx.drawRoundedRect,
91
+ drawGradientRect: ctx.drawGradientRect,
92
+
93
+ // Layout helpers
94
+ centerX: ctx.centerX,
95
+ centerY: ctx.centerY,
96
+ grid: ctx.grid,
97
+
98
+ // Mouse input
99
+ setMousePosition: ctx.setMousePosition,
100
+ setMouseButton: ctx.setMouseButton,
101
+ getMousePosition: ctx.getMousePosition,
102
+ isMouseDown: ctx.isMouseDown,
103
+ isMousePressed: ctx.isMousePressed,
104
+
105
+ // Color palette and font defs
106
+ uiColors: colors,
107
+ uiFonts: fonts,
108
+ });
109
+ },
110
+ };
111
+ }