noahlang 0.0.0-stage → 2.0.3
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/README.md +383 -2
- package/lib/Forge.noah +197 -0
- package/lib/style.css +41 -0
- package/noah.js +1319 -0
- package/package.json +46 -6
- package/scripts/postinstall.js +67 -0
package/README.md
CHANGED
|
@@ -1,3 +1,384 @@
|
|
|
1
|
-
#
|
|
1
|
+
# NoahLang for VS Code
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Syntax highlighting, snippets, and language support for **NoahLang** — a small, indentation-based teaching language.
|
|
4
|
+
|
|
5
|
+
> **Interpreter:** v1.1.0 · **Extension:** 0.1.0
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- Syntax highlighting for `.noah` files
|
|
10
|
+
- Snippets for common statements
|
|
11
|
+
- Variable declarations with `let`
|
|
12
|
+
- Variable reassignment with `set`
|
|
13
|
+
- Output with `write`
|
|
14
|
+
- Function definitions and calls
|
|
15
|
+
- `if` / `else` conditionals
|
|
16
|
+
- Arithmetic, comparison operators, strings, booleans, and `null`
|
|
17
|
+
|
|
18
|
+
> Hover documentation, live diagnostics, formatting, pipelines, loops, indexing, pattern-matching function clauses, and string interpolation are not documented as implemented features here because they are not supported by the interpreter source currently provided.
|
|
19
|
+
|
|
20
|
+
## Table of contents
|
|
21
|
+
|
|
22
|
+
- [Install](#install)
|
|
23
|
+
- [Quick start](#quick-start)
|
|
24
|
+
- [Language guide](#language-guide)
|
|
25
|
+
- [Comments](#comments)
|
|
26
|
+
- [Variables](#variables)
|
|
27
|
+
- [Values](#values)
|
|
28
|
+
- [Operators](#operators)
|
|
29
|
+
- [Conditionals](#conditionals)
|
|
30
|
+
- [Functions](#functions)
|
|
31
|
+
- [Built-in functions](#built-in-functions)
|
|
32
|
+
- [Examples](#examples)
|
|
33
|
+
- [CLI](#cli)
|
|
34
|
+
- [Error format](#error-format)
|
|
35
|
+
- [Cheat sheet](#cheat-sheet)
|
|
36
|
+
- [VS Code extension development](#-vs-code-extension-development)
|
|
37
|
+
- [Contributing](#contributing)
|
|
38
|
+
- [License](#license)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Install
|
|
43
|
+
|
|
44
|
+
### Run the interpreter
|
|
45
|
+
|
|
46
|
+
Make sure [Node.js](https://nodejs.org/) is installed. Save the interpreter as `noah.js`, then run a NoahLang file:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
node noah.js hello.noah
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
If the `noah` command has been installed and configured on your system, you can run:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
noah hello.noah
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### Install the VS Code extension from source
|
|
59
|
+
|
|
60
|
+
From the extension repository:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
git clone https://github.com/NoahKahenge/noahlang-vscode
|
|
64
|
+
cd noahlang-vscode
|
|
65
|
+
npm install
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Package and install the extension using the scripts configured in your repository. For example, if `vsce` and a `package` script are configured:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm run package
|
|
72
|
+
code --install-extension noahlang-0.1.0.vsix
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
The repository URL, script names, and generated VSIX filename should be checked against the actual project before running these commands.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Quick start
|
|
80
|
+
|
|
81
|
+
Create a file named `hello.noah`:
|
|
82
|
+
|
|
83
|
+
```noah
|
|
84
|
+
let name = "world"
|
|
85
|
+
write "Hello, " + name
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Run it:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
node noah.js hello.noah
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Expected output:
|
|
95
|
+
|
|
96
|
+
```text
|
|
97
|
+
Hello, world
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
## Language guide
|
|
103
|
+
|
|
104
|
+
NoahLang uses indentation to group statements inside functions and conditionals. Blocks begin with a colon (`:`), and their statements must be indented. Tabs are treated as four spaces by the interpreter.
|
|
105
|
+
|
|
106
|
+
### Comments
|
|
107
|
+
|
|
108
|
+
Use `//` for single-line comments. Comments can also appear after a statement.
|
|
109
|
+
|
|
110
|
+
```noah
|
|
111
|
+
// This is a comment
|
|
112
|
+
let x = 10 // This is a trailing comment
|
|
113
|
+
write x
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Variables
|
|
117
|
+
|
|
118
|
+
| Form | Meaning |
|
|
119
|
+
|---|---|
|
|
120
|
+
| `let name = expression` | Declare or assign a variable in the current environment. |
|
|
121
|
+
| `set name = expression` | Reassign an existing variable. The name must already exist in the current environment. |
|
|
122
|
+
|
|
123
|
+
Example:
|
|
124
|
+
|
|
125
|
+
```noah
|
|
126
|
+
let age = 42
|
|
127
|
+
set age = age + 1
|
|
128
|
+
write age
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Output:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
43
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Using `set` before declaring a variable with `let` produces an error:
|
|
138
|
+
|
|
139
|
+
```noah
|
|
140
|
+
set score = 100
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### Values
|
|
144
|
+
|
|
145
|
+
| Type | Examples |
|
|
146
|
+
|---|---|
|
|
147
|
+
| Number | `42`, `3.14`, `-7` |
|
|
148
|
+
| String | `"hello"`, `'hi'`, `"line\nbreak"` |
|
|
149
|
+
| Boolean | `true`, `false` |
|
|
150
|
+
| Null | `null` |
|
|
151
|
+
| Array-like value | `collect(1, 2, 3)` |
|
|
152
|
+
|
|
153
|
+
`collect(...)` currently returns a JavaScript array from the interpreter's built-in function. Array indexing and array-specific helper functions are not implemented in the provided interpreter.
|
|
154
|
+
|
|
155
|
+
### Operators
|
|
156
|
+
|
|
157
|
+
| Operators | Meaning |
|
|
158
|
+
|---|---|
|
|
159
|
+
| `(...)` | Grouping expressions |
|
|
160
|
+
| Unary `-`, `+` | Negative or positive numeric value |
|
|
161
|
+
| `*`, `/`, `%` | Multiplication, division, remainder |
|
|
162
|
+
| `+`, `-` | Addition or subtraction; `+` concatenates when either operand is a string |
|
|
163
|
+
| `==`, `!=` | Strict equality and inequality |
|
|
164
|
+
| `<`, `>`, `<=`, `>=` | Comparisons |
|
|
165
|
+
|
|
166
|
+
Division by zero raises a NoahLang error. Arithmetic operations convert operands to numbers where needed.
|
|
167
|
+
|
|
168
|
+
### Conditionals
|
|
169
|
+
|
|
170
|
+
Use `if` and `else` with an indented block:
|
|
171
|
+
|
|
172
|
+
```noah
|
|
173
|
+
let score = 72
|
|
174
|
+
|
|
175
|
+
if score >= 50:
|
|
176
|
+
write "Pass"
|
|
177
|
+
else:
|
|
178
|
+
write "Try again"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The condition is evaluated using JavaScript truthiness. Comparisons such as `score >= 50` return a Boolean.
|
|
182
|
+
|
|
183
|
+
The currently provided interpreter supports `if` and `else`. Chained `else if` and conditional expressions have not been implemented in this version.
|
|
184
|
+
|
|
185
|
+
### Functions
|
|
186
|
+
|
|
187
|
+
Define functions using `define name(parameters):` followed by an indented body.
|
|
188
|
+
|
|
189
|
+
```noah
|
|
190
|
+
define greet(name):
|
|
191
|
+
write "Hello, " + name
|
|
192
|
+
|
|
193
|
+
greet("Noah")
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
Output:
|
|
197
|
+
|
|
198
|
+
```text
|
|
199
|
+
Hello, Noah
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Functions can accept parameters and use `return` to return a value:
|
|
203
|
+
|
|
204
|
+
```noah
|
|
205
|
+
define add(a, b):
|
|
206
|
+
return a + b
|
|
207
|
+
|
|
208
|
+
let result = add(5, 7)
|
|
209
|
+
write result
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Output:
|
|
213
|
+
|
|
214
|
+
```text
|
|
215
|
+
12
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
A function that reaches the end without an explicit `return` returns `null`.
|
|
219
|
+
|
|
220
|
+
The current interpreter does not support the brace-based function syntax (`define greet() { ... }`) or the multi-clause pattern-matching syntax shown in some proposed NoahLang examples.
|
|
221
|
+
|
|
222
|
+
### Built-in functions
|
|
223
|
+
|
|
224
|
+
| Function | Current behavior |
|
|
225
|
+
|---|---|
|
|
226
|
+
| `write(...)` | The expression-level built-in prints its arguments separated by spaces. The `write expression` statement also prints a value. |
|
|
227
|
+
| `collect(a, b, ...)` | Returns the supplied arguments as an array. |
|
|
228
|
+
| `receive()` | Returns the next value supplied through the interpreter's `input` option, if one exists. Otherwise, it raises an error because interactive input is not enabled in this build. |
|
|
229
|
+
|
|
230
|
+
For example, `collect(1, 2, 3)` returns an array, but array indexing and functions such as `len`, `append`, and `remove` are not currently implemented.
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Examples
|
|
235
|
+
|
|
236
|
+
### Variable reassignment
|
|
237
|
+
|
|
238
|
+
```noah
|
|
239
|
+
let x = 5
|
|
240
|
+
set x = x + 1
|
|
241
|
+
write x
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
Output:
|
|
245
|
+
|
|
246
|
+
```text
|
|
247
|
+
6
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Conditional and reassignment
|
|
251
|
+
|
|
252
|
+
```noah
|
|
253
|
+
let x = 5
|
|
254
|
+
|
|
255
|
+
if x >= 5:
|
|
256
|
+
set x = x - 1
|
|
257
|
+
|
|
258
|
+
write x
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
Output:
|
|
262
|
+
|
|
263
|
+
```text
|
|
264
|
+
4
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Function with a return value
|
|
268
|
+
|
|
269
|
+
```noah
|
|
270
|
+
define square(n):
|
|
271
|
+
return n * n
|
|
272
|
+
|
|
273
|
+
write square(7)
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
Output:
|
|
277
|
+
|
|
278
|
+
```text
|
|
279
|
+
49
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### String concatenation
|
|
283
|
+
|
|
284
|
+
```noah
|
|
285
|
+
let name = "Noah"
|
|
286
|
+
write "Hello, " + name
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
Output:
|
|
290
|
+
|
|
291
|
+
```text
|
|
292
|
+
Hello, Noah
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
## CLI
|
|
298
|
+
|
|
299
|
+
```text
|
|
300
|
+
noah <file.noah> Run a NoahLang program
|
|
301
|
+
noah --version Display the interpreter version
|
|
302
|
+
noah -v Display the interpreter version
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
If you run the interpreter directly with Node.js:
|
|
306
|
+
|
|
307
|
+
```bash
|
|
308
|
+
node noah.js --version
|
|
309
|
+
node noah.js hello.noah
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The current interpreter prints `NoahLang 1.1.0` for the version command. It does not currently implement `--help`, interactive terminal input, or piped input for `receive()`.
|
|
313
|
+
|
|
314
|
+
### Exit codes
|
|
315
|
+
|
|
316
|
+
- `0`: successful execution or successful version display
|
|
317
|
+
- `1`: missing filename, missing file, or a language/runtime error
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
## Error format
|
|
322
|
+
|
|
323
|
+
Errors are printed to the terminal in this form:
|
|
324
|
+
|
|
325
|
+
```text
|
|
326
|
+
NoahLang error: Line 3: undefined variable 'score'
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
The interpreter reports the line number and error description. Column numbers and source-line caret diagnostics are not currently implemented.
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Cheat sheet
|
|
334
|
+
|
|
335
|
+
| Task | Syntax |
|
|
336
|
+
|---|---|
|
|
337
|
+
| Comment | `// note` |
|
|
338
|
+
| Declare a variable | `let x = 5` |
|
|
339
|
+
| Reassign a variable | `set x = 6` |
|
|
340
|
+
| Print a value | `write x` |
|
|
341
|
+
| Concatenate strings | `write "Hello, " + name` |
|
|
342
|
+
| Define a function | `define greet(name):` |
|
|
343
|
+
| Call a function | `greet("Noah")` |
|
|
344
|
+
| Return a value | `return value` |
|
|
345
|
+
| Conditional | `if condition:` |
|
|
346
|
+
| Alternative branch | `else:` |
|
|
347
|
+
| Create an array-like value | `collect(1, 2, 3)` |
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## VS Code extension development
|
|
352
|
+
|
|
353
|
+
This repository is intended to provide NoahLang language support in Visual Studio Code. The exact extension features depend on the files and contributions configured in the repository's `package.json`.
|
|
354
|
+
|
|
355
|
+
Common VS Code extension project files include:
|
|
356
|
+
|
|
357
|
+
- `package.json` — extension metadata and contributed features
|
|
358
|
+
- `syntaxes/noahlang.tmLanguage.json` — TextMate syntax grammar
|
|
359
|
+
- `snippets/noahlang.json` — editor snippets
|
|
360
|
+
- `language-configuration.json` — comments, brackets, and indentation settings
|
|
361
|
+
- `examples/` — sample `.noah` programs
|
|
362
|
+
|
|
363
|
+
Open the repository in VS Code and press **F5** to launch an Extension Development Host if the project has a debug configuration set up.
|
|
364
|
+
|
|
365
|
+
To package the extension, ensure the VS Code Extension Manager (`vsce`) and a packaging script are configured. Do not assume a particular script or output filename until you verify the repository's `package.json`.
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## Contributing
|
|
370
|
+
|
|
371
|
+
Issues and pull requests are welcome.
|
|
372
|
+
|
|
373
|
+
1. Create a branch for your change.
|
|
374
|
+
2. Keep documentation examples consistent with the interpreter's implemented syntax.
|
|
375
|
+
3. Add or update `.noah` examples when introducing language features.
|
|
376
|
+
4. Test examples using the interpreter before documenting them as supported.
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## License
|
|
381
|
+
|
|
382
|
+
MIT © Noah Kahenge
|
|
383
|
+
|
|
384
|
+
If the repository uses a different license, update this section to match the project's `LICENSE` file.
|
package/lib/Forge.noah
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// lib/Forge.noah — NoahLang web library (extended)
|
|
2
|
+
// Layout functions: nav, hero, cards, columns, footer.
|
|
3
|
+
// CSS lives in style.css (linked from done()).
|
|
4
|
+
// v2.0.2 syntax: every define ends with end define.
|
|
5
|
+
|
|
6
|
+
let __pageTitle = ""
|
|
7
|
+
let __pageBody = ""
|
|
8
|
+
let __nextId = 0
|
|
9
|
+
|
|
10
|
+
define escapeHtml(text)
|
|
11
|
+
let out = ""
|
|
12
|
+
let i = 0
|
|
13
|
+
while i < len(text)
|
|
14
|
+
let ch = text[i]
|
|
15
|
+
if ch == "<"
|
|
16
|
+
set out = out + "<"
|
|
17
|
+
else if ch == ">"
|
|
18
|
+
set out = out + ">"
|
|
19
|
+
else if ch == "&"
|
|
20
|
+
set out = out + "&"
|
|
21
|
+
else if ch == "\""
|
|
22
|
+
set out = out + """
|
|
23
|
+
else
|
|
24
|
+
set out = out + ch
|
|
25
|
+
end if
|
|
26
|
+
set i = i + 1
|
|
27
|
+
end while
|
|
28
|
+
return out
|
|
29
|
+
end define
|
|
30
|
+
|
|
31
|
+
define newId(prefix)
|
|
32
|
+
set __nextId = __nextId + 1
|
|
33
|
+
return prefix + "-" + str(__nextId)
|
|
34
|
+
end define
|
|
35
|
+
|
|
36
|
+
define addRaw(html)
|
|
37
|
+
set __pageBody = __pageBody + html + "\n"
|
|
38
|
+
end define
|
|
39
|
+
|
|
40
|
+
define page(name)
|
|
41
|
+
set __pageTitle = name
|
|
42
|
+
set __pageBody = ""
|
|
43
|
+
set __nextId = 0
|
|
44
|
+
end define
|
|
45
|
+
|
|
46
|
+
define head1(text)
|
|
47
|
+
let html = "<h1>" + escapeHtml(text) + "</h1>"
|
|
48
|
+
addRaw(html)
|
|
49
|
+
return html
|
|
50
|
+
end define
|
|
51
|
+
|
|
52
|
+
define head2(text)
|
|
53
|
+
let html = "<h2>" + escapeHtml(text) + "</h2>"
|
|
54
|
+
addRaw(html)
|
|
55
|
+
return html
|
|
56
|
+
end define
|
|
57
|
+
|
|
58
|
+
define head3(text)
|
|
59
|
+
let html = "<h3>" + escapeHtml(text) + "</h3>"
|
|
60
|
+
addRaw(html)
|
|
61
|
+
return html
|
|
62
|
+
end define
|
|
63
|
+
|
|
64
|
+
define para(content)
|
|
65
|
+
let html = "<p>" + escapeHtml(content) + "</p>"
|
|
66
|
+
addRaw(html)
|
|
67
|
+
return html
|
|
68
|
+
end define
|
|
69
|
+
|
|
70
|
+
define line(content)
|
|
71
|
+
let html = "<span>" + escapeHtml(content) + "</span>"
|
|
72
|
+
addRaw(html)
|
|
73
|
+
return html
|
|
74
|
+
end define
|
|
75
|
+
|
|
76
|
+
define code(content)
|
|
77
|
+
let html = "<pre><code>" + escapeHtml(content) + "</code></pre>"
|
|
78
|
+
addRaw(html)
|
|
79
|
+
return html
|
|
80
|
+
end define
|
|
81
|
+
|
|
82
|
+
define link(url, label)
|
|
83
|
+
let html = "<a href=\"" + escapeHtml(url) + "\">" + escapeHtml(label) + "</a>"
|
|
84
|
+
addRaw(html)
|
|
85
|
+
return html
|
|
86
|
+
end define
|
|
87
|
+
|
|
88
|
+
define button(label, eventId)
|
|
89
|
+
let handler = "fetch('/__event?id=" + escapeHtml(eventId) + "').then(function()\{location.reload()\})"
|
|
90
|
+
let html = "<button onclick=\"" + handler + "\">" + escapeHtml(label) + "</button>"
|
|
91
|
+
addRaw(html)
|
|
92
|
+
return html
|
|
93
|
+
end define
|
|
94
|
+
|
|
95
|
+
define section(className)
|
|
96
|
+
addRaw("<section class=\"" + escapeHtml(className) + "\">")
|
|
97
|
+
end define
|
|
98
|
+
|
|
99
|
+
define endSection()
|
|
100
|
+
addRaw("</section>")
|
|
101
|
+
end define
|
|
102
|
+
|
|
103
|
+
define container()
|
|
104
|
+
addRaw("<div class=\"container\">")
|
|
105
|
+
end define
|
|
106
|
+
|
|
107
|
+
define endContainer()
|
|
108
|
+
addRaw("</div>")
|
|
109
|
+
end define
|
|
110
|
+
|
|
111
|
+
define navStart(brand)
|
|
112
|
+
addRaw("<nav class=\"nav\"><div class=\"container nav-inner\">")
|
|
113
|
+
addRaw("<a class=\"brand\" href=\"/\">" + escapeHtml(brand) + "</a>")
|
|
114
|
+
addRaw("<div class=\"nav-links\">")
|
|
115
|
+
end define
|
|
116
|
+
|
|
117
|
+
define navItem(url, label)
|
|
118
|
+
addRaw("<a href=\"" + escapeHtml(url) + "\">" + escapeHtml(label) + "</a>")
|
|
119
|
+
end define
|
|
120
|
+
|
|
121
|
+
define navEnd()
|
|
122
|
+
addRaw("</div></div></nav>")
|
|
123
|
+
end define
|
|
124
|
+
|
|
125
|
+
define hero(title, subtitle, ctaLabel, ctaUrl)
|
|
126
|
+
addRaw("<header class=\"hero\"><div class=\"container\">")
|
|
127
|
+
addRaw("<h1>" + escapeHtml(title) + "</h1>")
|
|
128
|
+
addRaw("<p class=\"lead\">" + escapeHtml(subtitle) + "</p>")
|
|
129
|
+
if ctaLabel != "" and ctaUrl != ""
|
|
130
|
+
addRaw("<a class=\"btn\" href=\"" + escapeHtml(ctaUrl) + "\">" + escapeHtml(ctaLabel) + "</a>")
|
|
131
|
+
end if
|
|
132
|
+
addRaw("</div></header>")
|
|
133
|
+
end define
|
|
134
|
+
|
|
135
|
+
define cardsStart()
|
|
136
|
+
addRaw("<div class=\"cards\">")
|
|
137
|
+
end define
|
|
138
|
+
|
|
139
|
+
define card(title, body)
|
|
140
|
+
addRaw("<div class=\"card\">")
|
|
141
|
+
addRaw("<h3>" + escapeHtml(title) + "</h3>")
|
|
142
|
+
addRaw("<p>" + escapeHtml(body) + "</p>")
|
|
143
|
+
addRaw("</div>")
|
|
144
|
+
end define
|
|
145
|
+
|
|
146
|
+
define cardsEnd()
|
|
147
|
+
addRaw("</div>")
|
|
148
|
+
end define
|
|
149
|
+
|
|
150
|
+
define columnsStart()
|
|
151
|
+
addRaw("<div class=\"columns\">")
|
|
152
|
+
end define
|
|
153
|
+
|
|
154
|
+
define columnStart()
|
|
155
|
+
addRaw("<div class=\"column\">")
|
|
156
|
+
end define
|
|
157
|
+
|
|
158
|
+
define columnEnd()
|
|
159
|
+
addRaw("</div>")
|
|
160
|
+
end define
|
|
161
|
+
|
|
162
|
+
define columnsEnd()
|
|
163
|
+
addRaw("</div>")
|
|
164
|
+
end define
|
|
165
|
+
|
|
166
|
+
define bulletsStart()
|
|
167
|
+
addRaw("<ul>")
|
|
168
|
+
end define
|
|
169
|
+
|
|
170
|
+
define bullet(text)
|
|
171
|
+
addRaw("<li>" + escapeHtml(text) + "</li>")
|
|
172
|
+
end define
|
|
173
|
+
|
|
174
|
+
define bulletsEnd()
|
|
175
|
+
addRaw("</ul>")
|
|
176
|
+
end define
|
|
177
|
+
|
|
178
|
+
define footer(brand, year)
|
|
179
|
+
addRaw("<footer class=\"footer\"><div class=\"container\">")
|
|
180
|
+
addRaw("<p>© " + escapeHtml(str(year)) + " " + escapeHtml(brand) + ". All rights reserved.</p>")
|
|
181
|
+
addRaw("</div></footer>")
|
|
182
|
+
end define
|
|
183
|
+
|
|
184
|
+
define img(src, alt)
|
|
185
|
+
addRaw("<img src=\"" + escapeHtml(src) + "\" alt=\"" + escapeHtml(alt) + "\" class=\"img\">")
|
|
186
|
+
end define
|
|
187
|
+
|
|
188
|
+
define raw(html)
|
|
189
|
+
addRaw(html)
|
|
190
|
+
return html
|
|
191
|
+
end define
|
|
192
|
+
|
|
193
|
+
define done()
|
|
194
|
+
let html = "<!doctype html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\">\n<title>" + escapeHtml(__pageTitle) + "</title>\n<link rel=\"stylesheet\" href=\"style.css\">\n</head>\n<body>\n" + __pageBody + "</body>\n</html>"
|
|
195
|
+
writeToFile("index.html", html)
|
|
196
|
+
return html
|
|
197
|
+
end define
|
package/lib/style.css
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
2
|
+
body {
|
|
3
|
+
margin: 0;
|
|
4
|
+
font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
|
|
5
|
+
color: #1a1a1a;
|
|
6
|
+
line-height: 1.6;
|
|
7
|
+
background: #fafaf8;
|
|
8
|
+
}
|
|
9
|
+
.container { max-width: 960px; margin: 0 auto; padding: 0 24px; }
|
|
10
|
+
|
|
11
|
+
.nav { background: #fff; border-bottom: 1px solid #e5e5e5; position: sticky; top: 0; z-index: 10; }
|
|
12
|
+
.nav-inner { display: flex; justify-content: space-between; align-items: center; padding: 16px 24px; }
|
|
13
|
+
.brand { font-weight: 700; font-size: 1.1em; color: #1a1a1a; text-decoration: none; }
|
|
14
|
+
.nav-links a { color: #555; text-decoration: none; margin-left: 24px; font-size: 0.95em; }
|
|
15
|
+
.nav-links a:hover { color: #000; }
|
|
16
|
+
|
|
17
|
+
.hero { background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%); color: #fff; padding: 80px 0; text-align: center; }
|
|
18
|
+
.hero h1 { font-size: 2.5em; margin: 0 0 16px; }
|
|
19
|
+
.hero .lead { font-size: 1.2em; color: #ccc; margin: 0 0 32px; }
|
|
20
|
+
.btn { display: inline-block; background: #fff; color: #1a1a1a; padding: 12px 28px; border-radius: 6px; text-decoration: none; font-weight: 600; }
|
|
21
|
+
.btn:hover { transform: translateY(-1px); }
|
|
22
|
+
|
|
23
|
+
section { padding: 64px 0; }
|
|
24
|
+
h2 { font-size: 1.8em; margin: 0 0 24px; color: #1a1a1a; }
|
|
25
|
+
h3 { font-size: 1.2em; margin: 0 0 12px; }
|
|
26
|
+
p { margin: 0 0 16px; }
|
|
27
|
+
|
|
28
|
+
.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 24px; margin-top: 32px; }
|
|
29
|
+
.card { background: #fff; border: 1px solid #e5e5e5; border-radius: 8px; padding: 24px; }
|
|
30
|
+
.card:hover { box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
|
|
31
|
+
|
|
32
|
+
.columns { display: grid; grid-template-columns: 1fr 1fr; gap: 48px; align-items: start; }
|
|
33
|
+
@media (max-width: 640px) {
|
|
34
|
+
.columns { grid-template-columns: 1fr; }
|
|
35
|
+
.hero h1 { font-size: 1.8em; }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.footer { background: #1a1a1a; color: #999; padding: 32px 0; text-align: center; font-size: 0.9em; margin-top: 64px; }
|
|
39
|
+
.img { max-width: 100%; height: auto; border-radius: 8px; }
|
|
40
|
+
ul { padding-left: 20px; margin: 0 0 16px; }
|
|
41
|
+
li { margin-bottom: 8px; }
|