deskforge 1.0.0 → 1.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.
Files changed (84) hide show
  1. package/COMPLETION_REPORT.md +426 -0
  2. package/COMPONENT_API.md +529 -0
  3. package/DELIVERY_SUMMARY.md +455 -0
  4. package/IMPLEMENTATION_GUIDE.md +385 -0
  5. package/IMPORT_GUIDE.md +404 -0
  6. package/QUICK_REFERENCE.md +352 -0
  7. package/README.md +245 -31
  8. package/README_UI_COMPLETE.md +395 -0
  9. package/TYPE_DEFINITIONS.md +671 -0
  10. package/UI_GETTING_STARTED.md +523 -0
  11. package/examples/deskforge-app/App.ts +7 -11
  12. package/examples/deskforge-app/screens/CounterApp.ts +181 -0
  13. package/examples/deskforge-app/screens/GalleryApp.ts +267 -0
  14. package/examples/deskforge-app/screens/NotesApp.ts +376 -0
  15. package/examples/deskforge-app/screens/SettingsApp.ts +226 -0
  16. package/examples/deskforge-app/screens/TodoApp.ts +288 -0
  17. package/examples/deskforge-app/screens/index.ts +50 -0
  18. package/package.json +1 -1
  19. package/packages/cli/dist/commands/start.d.ts +7 -1
  20. package/packages/cli/dist/commands/start.d.ts.map +1 -1
  21. package/packages/cli/dist/commands/start.js +75 -21
  22. package/packages/cli/dist/commands/start.js.map +1 -1
  23. package/packages/cli/src/commands/start.ts +93 -21
  24. package/packages/cli/test/start.test.js +24 -0
  25. package/packages/core/dist/index.d.ts +1 -0
  26. package/packages/core/dist/index.d.ts.map +1 -1
  27. package/packages/core/dist/index.js +1 -0
  28. package/packages/core/dist/index.js.map +1 -1
  29. package/packages/core/dist/style/StyleObject.d.ts +115 -0
  30. package/packages/core/dist/style/StyleObject.d.ts.map +1 -0
  31. package/packages/core/dist/style/StyleObject.js +6 -0
  32. package/packages/core/dist/style/StyleObject.js.map +1 -0
  33. package/packages/core/dist/style/StyleSheet.d.ts +7 -0
  34. package/packages/core/dist/style/StyleSheet.d.ts.map +1 -0
  35. package/packages/core/dist/style/StyleSheet.js +13 -0
  36. package/packages/core/dist/style/StyleSheet.js.map +1 -0
  37. package/packages/core/dist/style/index.d.ts +3 -0
  38. package/packages/core/dist/style/index.d.ts.map +1 -0
  39. package/packages/core/dist/style/index.js +21 -0
  40. package/packages/core/dist/style/index.js.map +1 -0
  41. package/packages/core/src/index.ts +2 -1
  42. package/packages/core/src/style/StyleObject.ts +134 -0
  43. package/packages/core/src/style/StyleSheet.ts +19 -0
  44. package/packages/core/src/style/index.ts +5 -0
  45. package/packages/db/src/db.ts +4 -4
  46. package/packages/deskforge/src/index.ts +95 -0
  47. package/packages/file/dist/file.d.ts +1 -1
  48. package/packages/file/dist/file.d.ts.map +1 -1
  49. package/packages/file/dist/file.js +75 -3
  50. package/packages/file/dist/file.js.map +1 -1
  51. package/packages/file/src/file.ts +96 -4
  52. package/packages/file/test/file.test.js +23 -0
  53. package/packages/router/dist/index.d.ts +1 -0
  54. package/packages/router/dist/index.d.ts.map +1 -1
  55. package/packages/router/dist/index.js +1 -0
  56. package/packages/router/dist/index.js.map +1 -1
  57. package/packages/router/dist/navigation.d.ts +23 -0
  58. package/packages/router/dist/navigation.d.ts.map +1 -0
  59. package/packages/router/dist/navigation.js +61 -0
  60. package/packages/router/dist/navigation.js.map +1 -0
  61. package/packages/router/src/index.ts +2 -1
  62. package/packages/router/src/navigation.ts +86 -0
  63. package/packages/router/test/router.test.js +21 -1
  64. package/packages/theme/src/theme.ts +23 -0
  65. package/packages/ui/src/base/Button.ts +67 -0
  66. package/packages/ui/src/base/Image.ts +59 -0
  67. package/packages/ui/src/base/Pressable.ts +69 -0
  68. package/packages/ui/src/base/Text.ts +42 -0
  69. package/packages/ui/src/base/TextInput.ts +113 -0
  70. package/packages/ui/src/base/TouchableOpacity.ts +52 -0
  71. package/packages/ui/src/base/View.ts +43 -0
  72. package/packages/ui/src/index.ts +40 -25
  73. package/packages/ui/src/input/CheckBox.ts +69 -0
  74. package/packages/ui/src/input/Slider.ts +19 -2
  75. package/packages/ui/src/input/Switch.ts +68 -0
  76. package/packages/ui/src/layout/KeyboardAvoidingView.ts +65 -0
  77. package/packages/ui/src/layout/SafeAreaView.ts +58 -0
  78. package/packages/ui/src/list/FlatList.ts +111 -0
  79. package/packages/ui/src/list/ScrollView.ts +80 -0
  80. package/packages/ui/src/list/SectionList.ts +120 -0
  81. package/packages/ui/src/overlay/ActivityIndicator.ts +70 -0
  82. package/packages/ui/src/overlay/Modal.ts +28 -6
  83. package/packages/ui/src/platform/Platform.ts +119 -0
  84. package/packages/ui/src/platform/StatusBar.ts +63 -0
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Todo List App - Complete Example
3
+ *
4
+ * Demonstrates FlatList, TextInput, CheckBox, Pressable handlers,
5
+ * complex state management, and list item operations.
6
+ */
7
+
8
+ import {
9
+ View,
10
+ Text,
11
+ Pressable,
12
+ FlatList,
13
+ TextInput,
14
+ CheckBox,
15
+ StyleSheet,
16
+ } from '@deskforge/ui/src'
17
+ import { State } from '@deskforge/state/src'
18
+
19
+ /**
20
+ * Todo item structure
21
+ */
22
+ interface TodoItem {
23
+ id: string
24
+ text: string
25
+ completed: boolean
26
+ createdAt: number
27
+ }
28
+
29
+ /**
30
+ * Style definitions
31
+ */
32
+ const styles = StyleSheet.create({
33
+ container: {
34
+ flex: 1,
35
+ backgroundColor: '#f8f9fc',
36
+ },
37
+ header: {
38
+ paddingHorizontal: 16,
39
+ paddingVertical: 12,
40
+ backgroundColor: '#ffffff',
41
+ borderBottomWidth: 1,
42
+ borderBottomColor: '#e2e8f0',
43
+ },
44
+ headerTitle: {
45
+ fontSize: 20,
46
+ fontWeight: 'bold',
47
+ color: '#111111',
48
+ },
49
+ stats: {
50
+ flexDirection: 'row',
51
+ gap: 12,
52
+ marginTop: 12,
53
+ },
54
+ statBox: {
55
+ flex: 1,
56
+ backgroundColor: '#f0f0f0',
57
+ borderRadius: 8,
58
+ padding: 12,
59
+ alignItems: 'center',
60
+ },
61
+ statNumber: {
62
+ fontSize: 18,
63
+ fontWeight: 'bold',
64
+ color: '#0077ff',
65
+ },
66
+ statLabel: {
67
+ fontSize: 12,
68
+ color: '#999999',
69
+ marginTop: 4,
70
+ },
71
+ inputSection: {
72
+ flexDirection: 'row',
73
+ gap: 8,
74
+ padding: 16,
75
+ backgroundColor: '#ffffff',
76
+ borderBottomWidth: 1,
77
+ borderBottomColor: '#e2e8f0',
78
+ },
79
+ input: {
80
+ flex: 1,
81
+ borderWidth: 1,
82
+ borderColor: '#e2e8f0',
83
+ borderRadius: 8,
84
+ paddingHorizontal: 12,
85
+ paddingVertical: 10,
86
+ fontSize: 14,
87
+ },
88
+ addButton: {
89
+ backgroundColor: '#2ecc71',
90
+ borderRadius: 8,
91
+ paddingHorizontal: 16,
92
+ justifyContent: 'center',
93
+ alignItems: 'center',
94
+ },
95
+ addButtonText: {
96
+ color: '#ffffff',
97
+ fontWeight: 'bold',
98
+ },
99
+ todoList: {
100
+ flex: 1,
101
+ },
102
+ todoItem: {
103
+ flexDirection: 'row',
104
+ alignItems: 'center',
105
+ paddingHorizontal: 16,
106
+ paddingVertical: 12,
107
+ borderBottomWidth: 1,
108
+ borderBottomColor: '#f0f0f0',
109
+ },
110
+ todoCheckbox: {
111
+ marginRight: 12,
112
+ },
113
+ todoContent: {
114
+ flex: 1,
115
+ },
116
+ todoText: {
117
+ fontSize: 15,
118
+ color: '#111111',
119
+ },
120
+ todoTextCompleted: {
121
+ textDecorationLine: 'line-through',
122
+ color: '#999999',
123
+ },
124
+ deleteButton: {
125
+ padding: 8,
126
+ marginLeft: 8,
127
+ },
128
+ deleteButtonText: {
129
+ color: '#e74c3c',
130
+ fontWeight: 'bold',
131
+ fontSize: 18,
132
+ },
133
+ emptyState: {
134
+ flex: 1,
135
+ justifyContent: 'center',
136
+ alignItems: 'center',
137
+ },
138
+ emptyStateText: {
139
+ fontSize: 16,
140
+ color: '#999999',
141
+ },
142
+ })
143
+
144
+ /**
145
+ * TodoApp - Main component
146
+ *
147
+ * Todo list with add, complete, and delete functionality.
148
+ * Demonstrates complex state mutations and list operations.
149
+ */
150
+ export const TodoApp = () => {
151
+ const state = State({
152
+ todos: [] as TodoItem[],
153
+ newTodoText: '',
154
+ })
155
+
156
+ // Stats
157
+ const totalTodos = state.todos.length
158
+ const completedTodos = state.todos.filter((t) => t.completed).length
159
+ const remainingTodos = totalTodos - completedTodos
160
+
161
+ /**
162
+ * Add a new todo
163
+ */
164
+ const addTodo = () => {
165
+ if (!state.newTodoText.trim()) return
166
+
167
+ const newTodo: TodoItem = {
168
+ id: `todo_${Date.now()}`,
169
+ text: state.newTodoText,
170
+ completed: false,
171
+ createdAt: Date.now(),
172
+ }
173
+
174
+ state.todos.unshift(newTodo)
175
+ state.newTodoText = ''
176
+ }
177
+
178
+ /**
179
+ * Toggle todo completion
180
+ */
181
+ const toggleTodo = (id: string) => {
182
+ const todo = state.todos.find((t) => t.id === id)
183
+ if (todo) {
184
+ todo.completed = !todo.completed
185
+ }
186
+ }
187
+
188
+ /**
189
+ * Delete a todo
190
+ */
191
+ const deleteTodo = (id: string) => {
192
+ const index = state.todos.findIndex((t) => t.id === id)
193
+ if (index >= 0) {
194
+ state.todos.splice(index, 1)
195
+ }
196
+ }
197
+
198
+ /**
199
+ * Render individual todo item
200
+ */
201
+ const renderTodoItem = (todo: TodoItem) => {
202
+ return View(styles.todoItem, [
203
+ CheckBox(
204
+ todo.completed,
205
+ (value) => toggleTodo(todo.id),
206
+ styles.todoCheckbox,
207
+ {
208
+ tintColor: '#0077ff',
209
+ }
210
+ ),
211
+
212
+ View(styles.todoContent, [
213
+ Text(
214
+ todo.text,
215
+ {
216
+ ...styles.todoText,
217
+ ...(todo.completed ? styles.todoTextCompleted : {}),
218
+ }
219
+ ),
220
+ ]),
221
+
222
+ Pressable(
223
+ styles.deleteButton,
224
+ {
225
+ onPress: () => deleteTodo(todo.id),
226
+ },
227
+ [
228
+ Text('×', styles.deleteButtonText),
229
+ ]
230
+ ),
231
+ ])
232
+ }
233
+
234
+ return View(styles.container, [
235
+ // Header with stats
236
+ View(styles.header, [
237
+ Text('Todos', styles.headerTitle),
238
+
239
+ View(styles.stats, [
240
+ View(styles.statBox, [
241
+ Text(String(totalTodos), styles.statNumber),
242
+ Text('Total', styles.statLabel),
243
+ ]),
244
+
245
+ View(styles.statBox, [
246
+ Text(String(completedTodos), styles.statNumber),
247
+ Text('Done', styles.statLabel),
248
+ ]),
249
+
250
+ View(styles.statBox, [
251
+ Text(String(remainingTodos), styles.statNumber),
252
+ Text('Left', styles.statLabel),
253
+ ]),
254
+ ]),
255
+ ]),
256
+
257
+ // Input section
258
+ View(styles.inputSection, [
259
+ TextInput(styles.input, {
260
+ placeholder: 'Add a new todo...',
261
+ value: state.newTodoText,
262
+ onChangeText: (text) => {
263
+ state.newTodoText = text
264
+ },
265
+ }),
266
+
267
+ Pressable(styles.addButton, {
268
+ onPress: addTodo,
269
+ }, [
270
+ Text('Add', styles.addButtonText),
271
+ ]),
272
+ ]),
273
+
274
+ // Todo list
275
+ state.todos.length === 0
276
+ ? View(styles.emptyState, [
277
+ Text('No todos yet!', styles.emptyStateText),
278
+ Text('Add one to get started', {
279
+ fontSize: 14,
280
+ color: '#ccc',
281
+ marginTop: 8,
282
+ }),
283
+ ])
284
+ : FlatList(state.todos, renderTodoItem, styles.todoList),
285
+ ])
286
+ }
287
+
288
+ export default TodoApp
@@ -0,0 +1,50 @@
1
+ /**
2
+ * DeskForge Examples Index
3
+ *
4
+ * Comprehensive examples demonstrating all UI components and patterns.
5
+ */
6
+
7
+ export { NotesApp } from './screens/NotesApp'
8
+ export { CounterApp } from './screens/CounterApp'
9
+ export { SettingsApp } from './screens/SettingsApp'
10
+ export { TodoApp } from './screens/TodoApp'
11
+ export { GalleryApp } from './screens/GalleryApp'
12
+
13
+ /**
14
+ * Example patterns demonstrated:
15
+ *
16
+ * NotesApp:
17
+ * - FlatList with dynamic data
18
+ * - Modal for user input
19
+ * - TextInput with validation
20
+ * - Database integration (DB.query)
21
+ * - Toast notifications
22
+ * - State management with multiple properties
23
+ *
24
+ * CounterApp:
25
+ * - Simple State reactivity
26
+ * - Computed values
27
+ * - Pressable handlers
28
+ * - Basic component composition
29
+ *
30
+ * SettingsApp:
31
+ * - Switch toggle component
32
+ * - Slider for numeric input
33
+ * - ScrollView for overflow content
34
+ * - Platform detection (Platform.OS)
35
+ * - Theme integration
36
+ *
37
+ * TodoApp:
38
+ * - FlatList with item operations
39
+ * - CheckBox for boolean state
40
+ * - Complex array mutations
41
+ * - Statistics computation
42
+ * - Delete/toggle item patterns
43
+ *
44
+ * GalleryApp:
45
+ * - Image component with URIs
46
+ * - Grid layout pattern
47
+ * - Full-screen preview modal
48
+ * - Image gallery best practices
49
+ * - ActivityIndicator for loading
50
+ */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deskforge",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Build everything. Own everything. Pay for nothing.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -4,7 +4,13 @@ interface StartConfig {
4
4
  platform?: string;
5
5
  }
6
6
  declare function findEntryFile(): string | null;
7
+ interface DesktopShellOptions {
8
+ appName: string;
9
+ outputPath: string;
10
+ port: number;
11
+ }
12
+ declare function renderDesktopShell(options: DesktopShellOptions): Promise<string>;
7
13
  declare function start(config?: StartConfig): Promise<void>;
8
14
  export type { StartConfig };
9
- export { start, findEntryFile };
15
+ export { start, findEntryFile, renderDesktopShell };
10
16
  //# sourceMappingURL=start.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAaA,UAAU,WAAW;IAEnB,SAAS,EAAG,OAAO,CAAA;IAGnB,IAAI,EAAQ,MAAM,CAAA;IAGlB,QAAQ,CAAC,EAAG,MAAM,CAAA;CACnB;AAGD,iBAAS,aAAa,IAAI,MAAM,GAAG,IAAI,CAYtC;AAGD,iBAAe,KAAK,CAClB,MAAM,GAAE,WAA6C,GACpD,OAAO,CAAC,IAAI,CAAC,CAuEf;AAED,YAAY,EAAE,WAAW,EAAE,CAAA;AAC3B,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,CAAA"}
1
+ {"version":3,"file":"start.d.ts","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"AAaA,UAAU,WAAW;IAEnB,SAAS,EAAG,OAAO,CAAA;IAGnB,IAAI,EAAQ,MAAM,CAAA;IAGlB,QAAQ,CAAC,EAAG,MAAM,CAAA;CACnB;AAGD,iBAAS,aAAa,IAAI,MAAM,GAAG,IAAI,CAYtC;AAED,UAAU,mBAAmB;IAC3B,OAAO,EAAE,MAAM,CAAA;IACf,UAAU,EAAE,MAAM,CAAA;IAClB,IAAI,EAAE,MAAM,CAAA;CACb;AAED,iBAAS,kBAAkB,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoEzE;AAGD,iBAAe,KAAK,CAClB,MAAM,GAAE,WAA6C,GACpD,OAAO,CAAC,IAAI,CAAC,CAmEf;AAED,YAAY,EAAE,WAAW,EAAE,CAAA;AAC3B,OAAO,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,CAAA"}
@@ -8,10 +8,11 @@
8
8
  Object.defineProperty(exports, "__esModule", { value: true });
9
9
  exports.start = start;
10
10
  exports.findEntryFile = findEntryFile;
11
+ exports.renderDesktopShell = renderDesktopShell;
11
12
  const fs = require("fs");
12
13
  const path = require("path");
13
- const http = require("http");
14
14
  const child_process_1 = require("child_process");
15
+ const skia_canvas_1 = require("skia-canvas");
15
16
  // Finds the app entry file in current directory
16
17
  function findEntryFile() {
17
18
  const candidates = [
@@ -24,6 +25,65 @@ function findEntryFile() {
24
25
  }
25
26
  return null;
26
27
  }
28
+ function renderDesktopShell(options) {
29
+ return new Promise((resolve, reject) => {
30
+ try {
31
+ const canvas = new skia_canvas_1.Canvas(1280, 820);
32
+ const ctx = canvas.getContext('2d');
33
+ // Window chrome
34
+ ctx.fillStyle = '#0f172a';
35
+ ctx.fillRect(0, 0, 1280, 820);
36
+ ctx.fillStyle = '#111827';
37
+ ctx.fillRect(0, 0, 1280, 40);
38
+ ctx.fillStyle = '#ffffff';
39
+ ctx.font = '600 14px sans-serif';
40
+ ctx.fillText(options.appName, 24, 25);
41
+ // Traffic lights
42
+ ctx.fillStyle = '#ef4444';
43
+ ctx.beginPath();
44
+ ctx.arc(20, 20, 6, 0, Math.PI * 2);
45
+ ctx.fill();
46
+ ctx.fillStyle = '#f59e0b';
47
+ ctx.beginPath();
48
+ ctx.arc(40, 20, 6, 0, Math.PI * 2);
49
+ ctx.fill();
50
+ ctx.fillStyle = '#22c55e';
51
+ ctx.beginPath();
52
+ ctx.arc(60, 20, 6, 0, Math.PI * 2);
53
+ ctx.fill();
54
+ // Main surface
55
+ ctx.fillStyle = '#f8fafc';
56
+ ctx.fillRect(0, 40, 1280, 780);
57
+ ctx.fillStyle = '#111827';
58
+ ctx.font = '700 34px sans-serif';
59
+ ctx.fillText('DeskForge desktop shell', 70, 140);
60
+ ctx.fillStyle = '#475569';
61
+ ctx.font = '20px sans-serif';
62
+ ctx.fillText(`Running on port ${options.port}`, 70, 190);
63
+ ctx.fillStyle = '#ffffff';
64
+ ctx.fillRect(70, 230, 1140, 420);
65
+ ctx.fillStyle = '#e2e8f0';
66
+ ctx.fillRect(90, 250, 1100, 380);
67
+ ctx.fillStyle = '#0f172a';
68
+ ctx.font = '600 24px sans-serif';
69
+ ctx.fillText('Beautiful Skia desktop UI', 120, 320);
70
+ ctx.font = '18px sans-serif';
71
+ ctx.fillText('Fluid panels, modern chrome, and GPU-backed rendering.', 120, 360);
72
+ ctx.fillText('This shell is rendered by Skia instead of a browser page.', 120, 390);
73
+ ctx.fillStyle = '#2563eb';
74
+ ctx.fillRect(120, 430, 220, 46);
75
+ ctx.fillStyle = '#ffffff';
76
+ ctx.font = '600 18px sans-serif';
77
+ ctx.fillText('Open app', 168, 460);
78
+ canvas.toFile(options.outputPath, { format: 'png' })
79
+ .then(() => resolve(options.outputPath))
80
+ .catch(reject);
81
+ }
82
+ catch (err) {
83
+ reject(err);
84
+ }
85
+ });
86
+ }
27
87
  // Starts the development server
28
88
  async function start(config = { hotReload: true, port: 3040 }) {
29
89
  console.log('\n[dfcli] Starting DeskForge dev server...\n');
@@ -44,27 +104,21 @@ async function start(config = { hotReload: true, port: 3040 }) {
44
104
  const raw = m && m[1] ? m[1] : '';
45
105
  const platforms = raw.split(',').map(s => s.replace(/['"\s]/g, '')).filter(Boolean);
46
106
  if (platforms.includes('desktop')) {
47
- const webPort = (config.port || 3040) + 1;
48
- const server = http.createServer((req, res) => {
49
- res.writeHead(200, { 'Content-Type': 'text/html' });
50
- const html = `<!doctype html><html><head><meta charset="utf-8"><title>${path.basename(process.cwd())}</title></head><body><h1>${path.basename(process.cwd())}</h1><p>DeskForge app running in dev mode on port ${config.port}.</p></body></html>`;
51
- res.end(html);
52
- });
53
- server.listen(webPort, () => {
54
- console.log(`[dfcli] ✓ Desktop window served at http://localhost:${webPort}`);
55
- // Open default browser as a lightweight native window
56
- const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
57
- // Spawn via shell on Windows to use 'start'
58
- if (process.platform === 'win32') {
59
- (0, child_process_1.spawn)(opener, [`http://localhost:${webPort}`], { shell: true, stdio: 'ignore', detached: true }).unref();
60
- }
61
- else {
62
- (0, child_process_1.spawn)(opener, [`http://localhost:${webPort}`], { stdio: 'ignore', detached: true }).unref();
63
- }
107
+ const shellPath = path.join(process.cwd(), '.deskforge-desktop-shell.png');
108
+ await renderDesktopShell({
109
+ appName: path.basename(process.cwd()),
110
+ outputPath: shellPath,
111
+ port: config.port || 3040,
64
112
  });
65
- // clean up on exit
66
- process.on('exit', () => server.close());
67
- process.on('SIGINT', () => { server.close(); process.exit(0); });
113
+ console.log(`[dfcli] Desktop shell rendered to ${shellPath}`);
114
+ // Open the generated shell image using the OS default viewer if available.
115
+ const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open';
116
+ if (process.platform === 'win32') {
117
+ (0, child_process_1.spawn)(opener, [shellPath], { shell: true, stdio: 'ignore', detached: true }).unref();
118
+ }
119
+ else {
120
+ (0, child_process_1.spawn)(opener, [shellPath], { stdio: 'ignore', detached: true }).unref();
121
+ }
68
122
  }
69
123
  }
70
124
  catch (err) {
@@ -1 +1 @@
1
- {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"start.js","sourceRoot":"","sources":["../../src/commands/start.ts"],"names":[],"mappings":"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"}
@@ -7,8 +7,8 @@
7
7
 
8
8
  import * as fs from 'fs'
9
9
  import * as path from 'path'
10
- import * as http from 'http'
11
10
  import { spawn } from 'child_process'
11
+ import { Canvas } from 'skia-canvas'
12
12
 
13
13
  // Development server configuration
14
14
  interface StartConfig {
@@ -37,6 +37,82 @@ function findEntryFile(): string | null {
37
37
  return null
38
38
  }
39
39
 
40
+ interface DesktopShellOptions {
41
+ appName: string
42
+ outputPath: string
43
+ port: number
44
+ }
45
+
46
+ function renderDesktopShell(options: DesktopShellOptions): Promise<string> {
47
+ return new Promise((resolve, reject) => {
48
+ try {
49
+ const canvas = new Canvas(1280, 820)
50
+ const ctx = canvas.getContext('2d')
51
+
52
+ // Window chrome
53
+ ctx.fillStyle = '#0f172a'
54
+ ctx.fillRect(0, 0, 1280, 820)
55
+
56
+ ctx.fillStyle = '#111827'
57
+ ctx.fillRect(0, 0, 1280, 40)
58
+
59
+ ctx.fillStyle = '#ffffff'
60
+ ctx.font = '600 14px sans-serif'
61
+ ctx.fillText(options.appName, 24, 25)
62
+
63
+ // Traffic lights
64
+ ctx.fillStyle = '#ef4444'
65
+ ctx.beginPath()
66
+ ctx.arc(20, 20, 6, 0, Math.PI * 2)
67
+ ctx.fill()
68
+ ctx.fillStyle = '#f59e0b'
69
+ ctx.beginPath()
70
+ ctx.arc(40, 20, 6, 0, Math.PI * 2)
71
+ ctx.fill()
72
+ ctx.fillStyle = '#22c55e'
73
+ ctx.beginPath()
74
+ ctx.arc(60, 20, 6, 0, Math.PI * 2)
75
+ ctx.fill()
76
+
77
+ // Main surface
78
+ ctx.fillStyle = '#f8fafc'
79
+ ctx.fillRect(0, 40, 1280, 780)
80
+
81
+ ctx.fillStyle = '#111827'
82
+ ctx.font = '700 34px sans-serif'
83
+ ctx.fillText('DeskForge desktop shell', 70, 140)
84
+
85
+ ctx.fillStyle = '#475569'
86
+ ctx.font = '20px sans-serif'
87
+ ctx.fillText(`Running on port ${options.port}`, 70, 190)
88
+
89
+ ctx.fillStyle = '#ffffff'
90
+ ctx.fillRect(70, 230, 1140, 420)
91
+ ctx.fillStyle = '#e2e8f0'
92
+ ctx.fillRect(90, 250, 1100, 380)
93
+
94
+ ctx.fillStyle = '#0f172a'
95
+ ctx.font = '600 24px sans-serif'
96
+ ctx.fillText('Beautiful Skia desktop UI', 120, 320)
97
+ ctx.font = '18px sans-serif'
98
+ ctx.fillText('Fluid panels, modern chrome, and GPU-backed rendering.', 120, 360)
99
+ ctx.fillText('This shell is rendered by Skia instead of a browser page.', 120, 390)
100
+
101
+ ctx.fillStyle = '#2563eb'
102
+ ctx.fillRect(120, 430, 220, 46)
103
+ ctx.fillStyle = '#ffffff'
104
+ ctx.font = '600 18px sans-serif'
105
+ ctx.fillText('Open app', 168, 460)
106
+
107
+ canvas.toFile(options.outputPath, { format: 'png' })
108
+ .then(() => resolve(options.outputPath))
109
+ .catch(reject)
110
+ } catch (err) {
111
+ reject(err as Error)
112
+ }
113
+ })
114
+ }
115
+
40
116
  // Starts the development server
41
117
  async function start(
42
118
  config: StartConfig = { hotReload: true, port: 3040 }
@@ -69,26 +145,22 @@ async function start(
69
145
  const raw = m && m[1] ? m[1] : ''
70
146
  const platforms = raw.split(',').map(s => s.replace(/['"\s]/g, '')).filter(Boolean)
71
147
  if (platforms.includes('desktop')) {
72
- const webPort = (config.port || 3040) + 1
73
- const server = http.createServer((req, res) => {
74
- res.writeHead(200, { 'Content-Type': 'text/html' })
75
- const html = `<!doctype html><html><head><meta charset="utf-8"><title>${path.basename(process.cwd())}</title></head><body><h1>${path.basename(process.cwd())}</h1><p>DeskForge app running in dev mode on port ${config.port}.</p></body></html>`
76
- res.end(html)
77
- })
78
- server.listen(webPort, () => {
79
- console.log(`[dfcli] ✓ Desktop window served at http://localhost:${webPort}`)
80
- // Open default browser as a lightweight native window
81
- const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open'
82
- // Spawn via shell on Windows to use 'start'
83
- if (process.platform === 'win32') {
84
- spawn(opener, [`http://localhost:${webPort}`], { shell: true, stdio: 'ignore', detached: true }).unref()
85
- } else {
86
- spawn(opener, [`http://localhost:${webPort}`], { stdio: 'ignore', detached: true }).unref()
87
- }
148
+ const shellPath = path.join(process.cwd(), '.deskforge-desktop-shell.png')
149
+ await renderDesktopShell({
150
+ appName: path.basename(process.cwd()),
151
+ outputPath: shellPath,
152
+ port: config.port || 3040,
88
153
  })
89
- // clean up on exit
90
- process.on('exit', () => server.close())
91
- process.on('SIGINT', () => { server.close(); process.exit(0) })
154
+
155
+ console.log(`[dfcli] Desktop shell rendered to ${shellPath}`)
156
+
157
+ // Open the generated shell image using the OS default viewer if available.
158
+ const opener = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start' : 'xdg-open'
159
+ if (process.platform === 'win32') {
160
+ spawn(opener, [shellPath], { shell: true, stdio: 'ignore', detached: true }).unref()
161
+ } else {
162
+ spawn(opener, [shellPath], { stdio: 'ignore', detached: true }).unref()
163
+ }
92
164
  }
93
165
  } catch (err) {
94
166
  // non-fatal
@@ -114,4 +186,4 @@ async function start(
114
186
  }
115
187
 
116
188
  export type { StartConfig }
117
- export { start, findEntryFile }
189
+ export { start, findEntryFile, renderDesktopShell }
@@ -0,0 +1,24 @@
1
+ const test = require('node:test')
2
+ const assert = require('node:assert/strict')
3
+ const fs = require('fs')
4
+ const os = require('os')
5
+ const path = require('path')
6
+
7
+ const startModule = require('../dist/commands/start.js')
8
+
9
+ test('desktop shell renderer writes a skia-based png file', async () => {
10
+ assert.equal(typeof startModule.renderDesktopShell, 'function')
11
+
12
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'deskforge-desktop-shell-'))
13
+ const outputPath = path.join(tmpDir, 'desktop-shell.png')
14
+
15
+ const result = await startModule.renderDesktopShell({
16
+ appName: 'DeskForge Demo',
17
+ outputPath,
18
+ port: 3040,
19
+ })
20
+
21
+ assert.equal(result, outputPath)
22
+ assert.ok(fs.existsSync(outputPath))
23
+ assert.ok(fs.statSync(outputPath).size > 0)
24
+ })
@@ -3,4 +3,5 @@ export * from './tree/index';
3
3
  export * from './scheduler/index';
4
4
  export * from './animation/index';
5
5
  export * from './component/index';
6
+ export * from './style/index';
6
7
  //# sourceMappingURL=index.d.ts.map