nattoppet 2.1.0 → 4.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.
package/ppt.css ADDED
@@ -0,0 +1,304 @@
1
+ html {
2
+ font-family: sans-serif;
3
+ font-size: 20px;
4
+ line-height: 1.5;
5
+ min-height: 100vh;
6
+ color: #233;
7
+ }
8
+ @media (min-width: 1200px) {
9
+ html {
10
+ font-size: 24px;
11
+ }
12
+ }
13
+ body {
14
+ padding: 0;
15
+ margin: 0;
16
+ overflow-x: hidden;
17
+ overflow-y: scroll;
18
+ }
19
+ pre {
20
+ background: white;
21
+ border-top: 1px solid #eee;
22
+ border-bottom: 1px solid #eee;
23
+ padding: 20px;
24
+ font-size: 0.9rem;
25
+ line-height: 1.2;
26
+ text-align: left;
27
+ overflow-x: auto;
28
+ }
29
+ @media (min-width: 1200px) {
30
+ pre {
31
+ padding: 25px;
32
+ }
33
+ }
34
+ code {
35
+ font-family: monospace;
36
+ font-size: 0.9rem;
37
+ }
38
+ h2,
39
+ h3 {
40
+ color: #42b8a6;
41
+ }
42
+ h2 {
43
+ margin-top: 5vh;
44
+ font-size: 2rem;
45
+ }
46
+ h3 {
47
+ margin-top: 6vh;
48
+ font-size: 1.2rem;
49
+ }
50
+ p {
51
+ text-indent: 2rem;
52
+ }
53
+ blockquote p {
54
+ text-indent: 0;
55
+ }
56
+ img {
57
+ max-width: 100%;
58
+ }
59
+ img.full {
60
+ width: 100%;
61
+ }
62
+ img.small {
63
+ width: 40%;
64
+ }
65
+ @media (max-width: 625px) {
66
+ img.small {
67
+ width: 60%;
68
+ }
69
+ }
70
+ img.mid {
71
+ width: 60%;
72
+ }
73
+ img.big {
74
+ width: 80%;
75
+ }
76
+ li {
77
+ font-size: 1.2rem;
78
+ }
79
+ footer {
80
+ font-weight: 300;
81
+ font-size: 0.7rem;
82
+ color: #888;
83
+ }
84
+ .banner > h2 {
85
+ position: absolute;
86
+ left: 10vw;
87
+ top: 45%;
88
+ width: 80vw;
89
+ transform: translateY(-50%);
90
+ text-align: center;
91
+ margin: 0;
92
+ font-family: serif;
93
+ font-size: 3rem;
94
+ font-weight: normal;
95
+ }
96
+ @media (min-width: 1200px) {
97
+ .banner > h2 {
98
+ font-size: 3.2rem;
99
+ }
100
+ }
101
+ a {
102
+ text-decoration: none;
103
+ }
104
+ a:hover {
105
+ text-decoration: underline;
106
+ }
107
+ .scen {
108
+ position: relative;
109
+ margin: 0;
110
+ background-color: #eee;
111
+ transition: background-color 1s ease;
112
+ padding: 10vh 8vw;
113
+ width: 84vw;
114
+ min-height: 60vh;
115
+ }
116
+ .scen.active {
117
+ background-color: white;
118
+ }
119
+ .scen.wide {
120
+ width: 96vw;
121
+ padding: 10vh 2vw;
122
+ }
123
+ .scen::after {
124
+ content: "";
125
+ display: table;
126
+ clear: both;
127
+ }
128
+ @media (min-width: 1200px) {
129
+ .scen {
130
+ width: 76vw;
131
+ padding: 10vh 12vw;
132
+ }
133
+ }
134
+ .col-12 {
135
+ float: left;
136
+ width: 100%;
137
+ }
138
+ .col-11 {
139
+ float: left;
140
+ width: 91.66666667%;
141
+ }
142
+ .col-10 {
143
+ float: left;
144
+ width: 83.33333333%;
145
+ }
146
+ .col-9 {
147
+ float: left;
148
+ width: 75%;
149
+ }
150
+ .col-8 {
151
+ float: left;
152
+ width: 66.66666667%;
153
+ }
154
+ .col-7 {
155
+ float: left;
156
+ width: 58.33333333%;
157
+ }
158
+ .col-6 {
159
+ float: left;
160
+ width: 50%;
161
+ }
162
+ .col-5 {
163
+ float: left;
164
+ width: 41.66666667%;
165
+ }
166
+ .col-4 {
167
+ float: left;
168
+ width: 33.33333333%;
169
+ }
170
+ .col-3 {
171
+ float: left;
172
+ width: 25%;
173
+ }
174
+ .col-2 {
175
+ float: left;
176
+ width: 16.66666667%;
177
+ }
178
+ .col-1 {
179
+ float: left;
180
+ width: 8.33333333%;
181
+ }
182
+ .plum {
183
+ color: #ae81ff;
184
+ }
185
+ .fx-plum {
186
+ transition: color 400ms;
187
+ }
188
+ .fx-plum.fast {
189
+ transition: color 200ms;
190
+ }
191
+ .fx-plum.slow {
192
+ transition: color 600ms;
193
+ }
194
+ .fx-plum.active {
195
+ color: #ae81ff !important;
196
+ }
197
+ .chartreuse {
198
+ color: #a6e22e;
199
+ }
200
+ .fx-chartreuse {
201
+ transition: color 400ms;
202
+ }
203
+ .fx-chartreuse.fast {
204
+ transition: color 200ms;
205
+ }
206
+ .fx-chartreuse.slow {
207
+ transition: color 600ms;
208
+ }
209
+ .fx-chartreuse.active {
210
+ color: #a6e22e !important;
211
+ }
212
+ .cyan {
213
+ color: #66d9ef;
214
+ }
215
+ .fx-cyan {
216
+ transition: color 400ms;
217
+ }
218
+ .fx-cyan.fast {
219
+ transition: color 200ms;
220
+ }
221
+ .fx-cyan.slow {
222
+ transition: color 600ms;
223
+ }
224
+ .fx-cyan.active {
225
+ color: #66d9ef !important;
226
+ }
227
+ .tomato {
228
+ color: #f13c1a;
229
+ }
230
+ .fx-tomato {
231
+ transition: color 400ms;
232
+ }
233
+ .fx-tomato.fast {
234
+ transition: color 200ms;
235
+ }
236
+ .fx-tomato.slow {
237
+ transition: color 600ms;
238
+ }
239
+ .fx-tomato.active {
240
+ color: #f13c1a !important;
241
+ }
242
+ .moccasin {
243
+ color: #ffe4b5;
244
+ }
245
+ .fx-moccasin {
246
+ transition: color 400ms;
247
+ }
248
+ .fx-moccasin.fast {
249
+ transition: color 200ms;
250
+ }
251
+ .fx-moccasin.slow {
252
+ transition: color 600ms;
253
+ }
254
+ .fx-moccasin.active {
255
+ color: #ffe4b5 !important;
256
+ }
257
+ .slide {
258
+ transition: transform 400ms;
259
+ transform: translateX(-100vw);
260
+ }
261
+ .slide.fast {
262
+ transition: transform 200ms;
263
+ }
264
+ .slide.slow {
265
+ transition: transform 600ms;
266
+ }
267
+ .slide.active {
268
+ transform: translateX(0);
269
+ }
270
+ .fade {
271
+ transition: opacity 400ms;
272
+ opacity: 0;
273
+ }
274
+ .fade.fast {
275
+ transition: opacity 200ms;
276
+ }
277
+ .fade.slow {
278
+ transition: opacity 600ms;
279
+ }
280
+ .fade.active {
281
+ opacity: 1;
282
+ }
283
+ .rise {
284
+ transition: opacity 400ms;
285
+ transition: transform 400ms;
286
+ opacity: 0;
287
+ transform: translateY(10vh);
288
+ }
289
+ .rise.fast {
290
+ transition: opacity 200ms;
291
+ }
292
+ .rise.slow {
293
+ transition: opacity 600ms;
294
+ }
295
+ .rise.fast {
296
+ transition: transform 200ms;
297
+ }
298
+ .rise.slow {
299
+ transition: transform 600ms;
300
+ }
301
+ .rise.active {
302
+ opacity: 1;
303
+ transform: translateY(0);
304
+ }
package/ppt.js ADDED
@@ -0,0 +1,83 @@
1
+ // events: click, delay, sim
2
+ // time-for-delay: short, long
3
+ // fx: slide, fade, fx-[color]
4
+ // time-for-fx: fast, slow
5
+ var init;
6
+
7
+ init = function() {
8
+ var clickListener, current, enter, fx, inview, scens, timeline, update;
9
+ scens = [].slice.call(document.querySelectorAll('.scen'));
10
+ current = scens[0];
11
+ timeline = 0;
12
+ clickListener = function() {};
13
+ enter = function(x) {
14
+ x.classList.add('active');
15
+ return fx(++timeline, [].slice.call(x.querySelectorAll('.click, .delay, .sim')));
16
+ };
17
+ inview = function(x) {
18
+ var height, ref, top;
19
+ ({top, height} = x.getBoundingClientRect());
20
+ return (top <= (ref = innerHeight / 2) && ref <= top + height);
21
+ };
22
+ update = function() {
23
+ var e, i, j, len, len1, ref, results, x;
24
+ if (!inview(current)) {
25
+ results = [];
26
+ for (i = 0, len = scens.length; i < len; i++) {
27
+ x = scens[i];
28
+ if (inview(x)) {
29
+ ref = document.querySelectorAll('.active');
30
+ for (j = 0, len1 = ref.length; j < len1; j++) {
31
+ e = ref[j];
32
+ e.classList.remove('active');
33
+ }
34
+ enter(x);
35
+ current = x;
36
+ break;
37
+ } else {
38
+ results.push(void 0);
39
+ }
40
+ }
41
+ return results;
42
+ }
43
+ };
44
+ fx = function(t, [head, ...tail]) {
45
+ var cb;
46
+ if (head == null) {
47
+ return;
48
+ }
49
+ cb = function() {
50
+ if (t !== timeline) {
51
+ return;
52
+ }
53
+ head.classList.add('active');
54
+ return fx(t, tail);
55
+ };
56
+ if (head.classList.contains('click')) {
57
+ return clickListener = cb;
58
+ } else if (head.classList.contains('delay')) {
59
+ if (head.classList.contains('long')) {
60
+ return setTimeout(cb, 800);
61
+ } else if (head.classList.contains('short')) {
62
+ return setTimeout(cb, 200);
63
+ } else {
64
+ return setTimeout(cb, 400);
65
+ }
66
+ } else if (head.classList.contains('sim')) {
67
+ return cb();
68
+ }
69
+ };
70
+ enter(current);
71
+ addEventListener('click', function() {
72
+ return clickListener();
73
+ });
74
+ addEventListener('scroll', update);
75
+ addEventListener('resize', update);
76
+ return setInterval(update, 2000);
77
+ };
78
+
79
+ if (document.readyState === 'loading') {
80
+ document.addEventListener('DOMContentLoaded', init);
81
+ } else {
82
+ init();
83
+ }
@@ -1,3 +1,19 @@
1
+ <!doctype html>
2
+ <meta name=viewport content="width=device-width, initial-scale=1.0">
3
+ <meta name=author content="ylxdzsw@gmail.com">
4
+ <meta charset="utf-8">
5
+ <title>[title] | ylxdzsw's blog</title>
6
+
7
+ <style>
8
+ [mixin] ppt.css
9
+ </style>
10
+
11
+ <script>
12
+ [mixin] ppt.js
13
+ </script>
14
+
15
+ [#slot]
16
+
1
17
  [eval] this.inited ? "</section>" : ''
2
18
 
3
19
  [section]=
@@ -18,4 +34,4 @@ const content = capture_until('\n')
18
34
  const {args:[length]} = std_call()
19
35
  ;`<div style="height: ${length}; min-width: 1px"></div>`
20
36
 
21
- [mixin] @std/common.ymd
37
+ [mixin] common.ymd
package/readme.md CHANGED
@@ -1,10 +1,46 @@
1
1
  nattoppet
2
2
  =========
3
3
 
4
- A tiny markup language and several themes for making documents and slides as single-file bundled html.
4
+ A tiny markup language and themes for making documents, slides, or web apps as single-file bundled html or executables.
5
5
 
6
- ### Usage (no installation required)
6
+ ### Usage
7
7
 
8
+ Compile a nattoppet file:
9
+
10
+ ```bash
11
+ bun run nattoppet.ts in.ymd > out.html
12
+ ```
13
+
14
+ Compile a nattoppet file but don't minimize (for debugging):
15
+
16
+ ```bash
17
+ bun run nattoppet.ts in.ymd --dev > out.html
8
18
  ```
9
- $ npx nattoppet in.ymd > out.html
19
+
20
+ Open a browser to preview a nattoppet file; rebuild every time the page is refreshed:
21
+
22
+ ```bash
23
+ bun run nattoppet-dev.ts in.ymd
10
24
  ```
25
+
26
+ Initialize a new project:
27
+
28
+ ```bash
29
+ bun run nattoppet-init.ts form
30
+ ```
31
+
32
+ Build a double-clickable executable for the page:
33
+
34
+ ```bash
35
+ bun run nattoppet-native.ts init
36
+ # (edit the metadata as needed)
37
+ bun run nattoppet-native.ts build in.ymd
38
+ ```
39
+
40
+
41
+ ### History
42
+
43
+ - 1.0: This project started as an experiment of co.js to replicate gulp.js, when async/await was not a thing and wind.js was the cool guy that everyone was talking about.
44
+ - 2.0: Later I decided to build ymd.js, a custom LaTeX-style macro-based markup language for blogging, and turned nattoppet a bundler. Promise landed on JavaScript, and async/await became available with babel.
45
+ - 3.0: I became a minimalist and stripped down all dependencies, merged ymd.js and nattoppet, and flattened the directory. I have also migrated to Deno to further remove the nodejs clutters.
46
+ - 4.0: AI came. As an experimental project, nattoppet was put to test AI. Kimi 2.5 successfully ported it from Deno to Bun in half an hour, with all posts on my blog builds without error. It made me feel empty, but I have to embrace it. This project will be fully vibed from now on.
package/stdlib.ts ADDED
@@ -0,0 +1,120 @@
1
+ import * as path from "node:path"
2
+ import * as fs from "node:fs"
3
+ import * as zlib from "node:zlib"
4
+
5
+ import { marked } from "marked"
6
+ import coffee from "coffeescript"
7
+ import katex from "katex"
8
+ import less from "less"
9
+
10
+ function compress_sync(data: ArrayBuffer) {
11
+ // Keep using zlib.deflateRawSync for browser compatibility
12
+ // Browser uses DecompressionStream("deflate-raw") which expects raw deflate
13
+ return zlib.deflateRawSync(new Uint8Array(data), {
14
+ flush: zlib.constants.Z_FINISH,
15
+ level: zlib.constants.Z_BEST_COMPRESSION
16
+ })
17
+ }
18
+
19
+ export default {
20
+ skip(n: number) {
21
+ this.remaining = this.remaining.substring(n)
22
+ },
23
+
24
+ capture_until(delimiter: string) {
25
+ const p = this.remaining.indexOf(delimiter)
26
+ if (p < 0) {
27
+ const result = this.remaining
28
+ this.remaining = ""
29
+ return result
30
+ }
31
+ const result = this.remaining.substring(0, p)
32
+ this.remaining = this.remaining.substring(p + delimiter.length)
33
+ return result
34
+ },
35
+
36
+ std_call(hascontent = false) {
37
+ const opts: string[] = [], args: string[] = []
38
+ const parse_option = () => {
39
+ const m = this.remaining.match(/^\.([\w\-]+)/)
40
+ this.skip(m[0].length)
41
+ opts.push(m[1])
42
+ }
43
+ const parse_argument = () => {
44
+ const m = this.remaining.match(/^\((.*?)\)|{(.*?)}/)
45
+ this.skip(m[0].length)
46
+ args.push(m[1] || m[2])
47
+ }
48
+ const parse_block = () => {
49
+ const m = this.remaining.match(/^(>+)\n?/)
50
+ this.skip(m[0].length)
51
+ return this.capture_until('<'.repeat(m[1].length))
52
+ }
53
+
54
+ while (true) {
55
+ switch (this.remaining[0]) {
56
+ case '.':
57
+ parse_option()
58
+ break
59
+ case '(':
60
+ case '{':
61
+ parse_argument()
62
+ break
63
+ case '>':
64
+ return { opts, args, block: parse_block() }
65
+ case ' ':
66
+ if (hascontent)
67
+ return { opts, args, block: this.capture_until('\n').slice(1) }
68
+ default:
69
+ return { opts, args }
70
+ }
71
+ }
72
+ },
73
+
74
+ rpath(file: string) {
75
+ if (!path.isAbsolute(file))
76
+ return path.join(this.base_dir, file)
77
+ return file
78
+ },
79
+
80
+ read(file: string, encoding = "utf-8") {
81
+ const filePath = this.rpath(file)
82
+ switch (encoding) {
83
+ case "utf8":
84
+ case "utf-8":
85
+ return fs.readFileSync(filePath, 'utf-8')
86
+ case "base64":
87
+ return Buffer.from(fs.readFileSync(filePath)).toString('base64')
88
+ case "compressed-base64":
89
+ return Buffer.from(compress_sync(fs.readFileSync(filePath))).toString('base64')
90
+ default:
91
+ throw "unknown encoding: " + encoding
92
+ }
93
+ },
94
+
95
+ extname(file: string) {
96
+ return path.extname(file).slice(1)
97
+ },
98
+
99
+ basename(file: string) {
100
+ return path.basename(file)
101
+ },
102
+
103
+ render_coffee(str: string, options: any) {
104
+ return coffee.compile(str, options)
105
+ },
106
+
107
+ render_less(str: string) {
108
+ let x: any; less.render(str, {}, (err: any, out: any) => !err && (x = out))
109
+ if (!x) throw "less failed to compile synchronously"
110
+ return x.css
111
+ },
112
+
113
+ render_markdown(str: string) {
114
+ return marked.parse(str)
115
+ },
116
+
117
+ render_katex(str: string, displayMode = false) {
118
+ return katex.renderToString(str, { displayMode, output: 'html' })
119
+ }
120
+ } as any
package/tml.css ADDED
@@ -0,0 +1,125 @@
1
+ html {
2
+ font-family: sans-serif;
3
+ font-size: 18px;
4
+ line-height: 1.5;
5
+ text-align: justify;
6
+ color: #33333d;
7
+ -webkit-user-select: none;
8
+ hyphens: auto;
9
+ }
10
+ @media (max-width: 625px) {
11
+ html {
12
+ font-size: 14px;
13
+ }
14
+ }
15
+ body {
16
+ margin: 15px 0;
17
+ }
18
+ @media (min-width: 1200px) {
19
+ body {
20
+ height: calc(100vh - 45px);
21
+ column-width: 540px;
22
+ column-gap: 0;
23
+ column-fill: auto;
24
+ }
25
+ }
26
+ article {
27
+ margin: 40px 0 0 15px;
28
+ padding: 0 20px;
29
+ }
30
+ @media (max-width: 625px) {
31
+ article {
32
+ margin-left: 8px;
33
+ padding-left: 10px;
34
+ }
35
+ }
36
+ article:first-child {
37
+ margin-top: 0;
38
+ }
39
+ article:nth-child(3n) {
40
+ border-left: solid #ECE335;
41
+ }
42
+ article:nth-child(3n+1) {
43
+ border-left: solid #1CBF5F;
44
+ }
45
+ article:nth-child(3n+2) {
46
+ border-left: solid #2C8DD5;
47
+ }
48
+ h2 {
49
+ position: relative;
50
+ margin: 0;
51
+ padding: 0.8rem 0 0.7rem;
52
+ break-inside: avoid;
53
+ border-bottom: 0.5px solid #ddd;
54
+ }
55
+ h2:before {
56
+ top: 0.9rem;
57
+ font-size: 1.4rem;
58
+ }
59
+ @media (max-width: 625px) {
60
+ h2:before {
61
+ left: -0.8rem;
62
+ top: 1rem;
63
+ }
64
+ }
65
+ h2:hover:before {
66
+ opacity: 1;
67
+ }
68
+ h2 a {
69
+ color: #2c3e50;
70
+ }
71
+ h3 {
72
+ position: relative;
73
+ margin: 0;
74
+ padding: 0.8rem 0 0;
75
+ break-inside: avoid;
76
+ }
77
+ p {
78
+ margin: 1.2rem 0;
79
+ }
80
+ pre {
81
+ background-color: #fafaf9;
82
+ }
83
+ code {
84
+ font-family: monospace;
85
+ font-size: 0.8rem;
86
+ padding: 0.1rem;
87
+ }
88
+ a {
89
+ color: #42b983;
90
+ text-decoration: none;
91
+ }
92
+ a:hover {
93
+ text-decoration: underline;
94
+ }
95
+ blockquote {
96
+ margin: 2rem 0;
97
+ padding: 0 1.2rem;
98
+ font-style: italic;
99
+ }
100
+ blockquote p {
101
+ margin-left: 0;
102
+ }
103
+ img {
104
+ break-inside: avoid;
105
+ }
106
+ .full {
107
+ display: block;
108
+ margin: 1rem 0;
109
+ width: 100%;
110
+ }
111
+ .half {
112
+ display: block;
113
+ margin: 1rem 0;
114
+ width: 50%;
115
+ }
116
+ .end-symbol {
117
+ text-align: center;
118
+ display: none;
119
+ margin-top: 1rem;
120
+ }
121
+ @media (min-width: 1200px) {
122
+ .end-symbol {
123
+ display: block;
124
+ }
125
+ }
package/tml.js ADDED
@@ -0,0 +1,3 @@
1
+ addEventListener('wheel', function(e) {
2
+ return scrollBy(e.deltaY, 0);
3
+ });