firefly-compiler 0.4.77 → 0.4.78
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/core/Json.ff +2 -2
- package/core/String.ff +16 -0
- package/fireflysite/.firefly/package.ff +4 -0
- package/fireflysite/CommunityOverview.ff +20 -0
- package/fireflysite/CountingButtonDemo.ff +58 -0
- package/fireflysite/ExamplesOverview.ff +31 -0
- package/fireflysite/Guide.ff +308 -0
- package/fireflysite/GuideIntroduction.ff +69 -0
- package/fireflysite/Main.ff +82 -5
- package/fireflysite/MatchingPasswordsDemo.ff +86 -0
- package/fireflysite/PackagesOverview.ff +49 -0
- package/fireflysite/Styles.ff +306 -0
- package/fireflysite/firefly-logo-notext.png +0 -0
- package/lux/Main2.ff +2 -3
- package/output/js/ff/core/Json.mjs +2 -2
- package/output/js/ff/core/String.mjs +20 -0
- package/package.json +1 -1
- package/vscode/package.json +1 -1
package/core/Json.ff
CHANGED
|
@@ -184,7 +184,7 @@ extend self: Json {
|
|
|
184
184
|
|
|
185
185
|
index(key: Int): Json
|
|
186
186
|
target js sync """
|
|
187
|
-
return
|
|
187
|
+
return Array.isArray(self_) ? self_[key] ?? null : null;
|
|
188
188
|
"""
|
|
189
189
|
|
|
190
190
|
hasField(key: String): Bool
|
|
@@ -200,7 +200,7 @@ extend self: Json {
|
|
|
200
200
|
|
|
201
201
|
getIndex(key: Int): Option[Json]
|
|
202
202
|
target js sync """
|
|
203
|
-
return
|
|
203
|
+
return Array.isArray(self_) ? ff_core_Option.Some(self_[key_] ?? null) : ff_core_Option.None();
|
|
204
204
|
"""
|
|
205
205
|
|
|
206
206
|
getFields(): Option[List[String]]
|
package/core/String.ff
CHANGED
|
@@ -206,6 +206,22 @@ extend self: String {
|
|
|
206
206
|
return true;
|
|
207
207
|
"""
|
|
208
208
|
|
|
209
|
+
filter(body: Char => Bool): String
|
|
210
|
+
target js sync """
|
|
211
|
+
const result = [];
|
|
212
|
+
for(let i = 0; i < self_.length; i++) {
|
|
213
|
+
if(body_(self_.charCodeAt(i))) result.push(self_.charAt(i));
|
|
214
|
+
}
|
|
215
|
+
return result.join("");
|
|
216
|
+
"""
|
|
217
|
+
target js async """
|
|
218
|
+
const result = [];
|
|
219
|
+
for(let i = 0; i < self_.length; i++) {
|
|
220
|
+
if(await body_(self_.charCodeAt(i))) result.push(self_.charAt(i));
|
|
221
|
+
}
|
|
222
|
+
return result.join("");
|
|
223
|
+
"""
|
|
224
|
+
|
|
209
225
|
toBuffer(): Buffer
|
|
210
226
|
target js sync """
|
|
211
227
|
const encoded = new TextEncoder().encode(self_)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
import CountingButtonDemo
|
|
3
|
+
import MatchingPasswordsDemo
|
|
4
|
+
|
|
5
|
+
new(): Document {
|
|
6
|
+
Document([
|
|
7
|
+
Section("Community", [
|
|
8
|
+
Paragraph([
|
|
9
|
+
Text("The developers of Firefly hang out in")
|
|
10
|
+
Code("#firefly")
|
|
11
|
+
Text("on the")
|
|
12
|
+
Link("r/ProgrammingLanguages", "https://discord.gg/yQ9mSk7CJ5")
|
|
13
|
+
Text("Discord.")
|
|
14
|
+
])
|
|
15
|
+
Paragraph([
|
|
16
|
+
Text("If you're interested in Firefly, that's the best way to be part of the community for now.")
|
|
17
|
+
])
|
|
18
|
+
])
|
|
19
|
+
])
|
|
20
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
import Lux from ff:lux
|
|
3
|
+
import LuxEvent from ff:lux
|
|
4
|
+
|
|
5
|
+
name = "Counting button"
|
|
6
|
+
|
|
7
|
+
new(): Demo {
|
|
8
|
+
Demo(
|
|
9
|
+
name
|
|
10
|
+
{render(_)}
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
render(lux: Lux): Unit {
|
|
15
|
+
lux.div {
|
|
16
|
+
lux.useState(0): count, setCount =>
|
|
17
|
+
lux.button {
|
|
18
|
+
lux.text("Clicked " + count + " times")
|
|
19
|
+
lux.onClick {event =>
|
|
20
|
+
event.preventDefault()
|
|
21
|
+
setCount(count + 1)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
newDocument(): Document {
|
|
28
|
+
Document([
|
|
29
|
+
Section(name, [
|
|
30
|
+
Paragraph([Text("A button that counts how many times it's been clicked.")])
|
|
31
|
+
])
|
|
32
|
+
Section("Demo", [
|
|
33
|
+
LuxDemo(name)
|
|
34
|
+
])
|
|
35
|
+
Section("Code", [
|
|
36
|
+
Paragraph([
|
|
37
|
+
Text("This example uses the Lux frontend framework.")
|
|
38
|
+
Text("In Lux, state lives in the DOM, and the")
|
|
39
|
+
Code("useState")
|
|
40
|
+
Text("method lets you maintain state in the parent node.")
|
|
41
|
+
])
|
|
42
|
+
CodeBlock("""
|
|
43
|
+
render(lux: Lux): Unit {
|
|
44
|
+
lux.div {
|
|
45
|
+
lux.useState(0): count, setCount =>
|
|
46
|
+
lux.button {
|
|
47
|
+
lux.text("Clicked " + count + " times")
|
|
48
|
+
lux.onClick {event =>
|
|
49
|
+
event.preventDefault()
|
|
50
|
+
setCount(count + 1)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
""", firefly = True)
|
|
56
|
+
])
|
|
57
|
+
])
|
|
58
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
import CountingButtonDemo
|
|
3
|
+
import MatchingPasswordsDemo
|
|
4
|
+
|
|
5
|
+
mock(): List[Document] {
|
|
6
|
+
[
|
|
7
|
+
new()
|
|
8
|
+
CountingButtonDemo.newDocument()
|
|
9
|
+
MatchingPasswordsDemo.newDocument()
|
|
10
|
+
]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
new(): Document {
|
|
14
|
+
Document([
|
|
15
|
+
Section("Overview", [
|
|
16
|
+
Paragraph([
|
|
17
|
+
Text("Here you'll find examples that demonstrate what you can do with Firefly.")
|
|
18
|
+
Text("In fact, you're looking at an example right now - this site is")
|
|
19
|
+
Link("written in Firefly", "https://github.com/Ahnfelt/firefly-boot/tree/master/fireflysite")
|
|
20
|
+
Text("and so is the")
|
|
21
|
+
Link("Firefly compiler", "https://github.com/Ahnfelt/firefly-boot/tree/master/compiler")
|
|
22
|
+
Text("and")
|
|
23
|
+
Link("language server", "https://github.com/Ahnfelt/firefly-boot/tree/master/lsp")
|
|
24
|
+
Text(".")
|
|
25
|
+
])
|
|
26
|
+
Paragraph([
|
|
27
|
+
Text("Check the list here for examples and demos.")
|
|
28
|
+
])
|
|
29
|
+
])
|
|
30
|
+
])
|
|
31
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import Lux from ff:lux
|
|
2
|
+
import Css from ff:lux
|
|
3
|
+
import Tokenizer from ff:compiler
|
|
4
|
+
import Token from ff:compiler
|
|
5
|
+
import Styles
|
|
6
|
+
|
|
7
|
+
data Guide(
|
|
8
|
+
documents: List[Document]
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
data Document(
|
|
12
|
+
sections: List[Section]
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
data Section(
|
|
16
|
+
heading: String
|
|
17
|
+
blocks: List[Block]
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
data Block {
|
|
21
|
+
Paragraph(inlines: List[Inline])
|
|
22
|
+
Bullets(items: List[List[Inline]])
|
|
23
|
+
CodeBlock(code: String, firefly: Bool = False)
|
|
24
|
+
Image(url: String)
|
|
25
|
+
Video(url: String)
|
|
26
|
+
LuxDemo(demo: String)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
data Inline {
|
|
30
|
+
Text(text: String)
|
|
31
|
+
Bold(text: String)
|
|
32
|
+
Italic(text: String)
|
|
33
|
+
Code(code: String, firefly: Bool = False)
|
|
34
|
+
Link(text: String, url: String)
|
|
35
|
+
Anchor(heading: String, title: Option[String] = None)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
capability Demo(
|
|
39
|
+
name: String
|
|
40
|
+
render: Lux => Unit
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
render(lux: Lux, prefix: String, kebab: String, guide: Guide, demos: List[Demo]) {
|
|
44
|
+
let document = guide.documents.find {
|
|
45
|
+
_.sections.first().any {kebabCase(_.heading) == kebab}
|
|
46
|
+
}.else {guide.documents.grabFirst()}
|
|
47
|
+
lux.add("div") {
|
|
48
|
+
lux.cssClass(Styles.pageCss)
|
|
49
|
+
renderTopbar(lux, prefix)
|
|
50
|
+
lux.add("div") {
|
|
51
|
+
lux.cssClass(Styles.guideCss)
|
|
52
|
+
renderSidebar(lux, prefix, guide, document.sections.first().map {_.heading}.else {""})
|
|
53
|
+
lux.add("main") {
|
|
54
|
+
lux.cssClass(Styles.guideMainCss)
|
|
55
|
+
renderDocument(lux, document, demos)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
renderSoleDocument(lux: Lux, prefix: String, document: Document, demos: List[Demo]) {
|
|
62
|
+
lux.add("div") {
|
|
63
|
+
lux.cssClass(Styles.pageCss)
|
|
64
|
+
renderTopbar(lux, prefix)
|
|
65
|
+
lux.add("main") {
|
|
66
|
+
lux.cssClass(Styles.guideSoleDocumentCss)
|
|
67
|
+
renderDocument(lux, document, demos)
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
renderTopbar(lux: Lux, prefix: String) {
|
|
73
|
+
lux.add("header") {
|
|
74
|
+
lux.add("nav") {
|
|
75
|
+
lux.cssClass(Styles.topbarCss)
|
|
76
|
+
lux.add("a") {
|
|
77
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
78
|
+
lux.cssClass(Styles.topbarFireflyCss)
|
|
79
|
+
if(prefix == "/") {lux.set("aria-current", "page")}
|
|
80
|
+
lux.set("href", "/")
|
|
81
|
+
lux.text("Firefly")
|
|
82
|
+
}
|
|
83
|
+
lux.add("a") {
|
|
84
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
85
|
+
if(prefix == "/guide/") {lux.set("aria-current", "page")}
|
|
86
|
+
lux.set("href", "/guide/")
|
|
87
|
+
lux.text("Guide")
|
|
88
|
+
}
|
|
89
|
+
lux.add("a") {
|
|
90
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
91
|
+
if(prefix == "/examples/") {lux.set("aria-current", "page")}
|
|
92
|
+
lux.set("href", "/examples/")
|
|
93
|
+
lux.text("Examples")
|
|
94
|
+
}
|
|
95
|
+
lux.add("a") {
|
|
96
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
97
|
+
if(prefix == "/packages/") {lux.set("aria-current", "page")}
|
|
98
|
+
lux.set("href", "/packages/")
|
|
99
|
+
lux.text("Packages")
|
|
100
|
+
}
|
|
101
|
+
lux.add("a") {
|
|
102
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
103
|
+
if(prefix == "/community/") {lux.set("aria-current", "page")}
|
|
104
|
+
lux.set("href", "/community/")
|
|
105
|
+
lux.text("Community")
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
renderSidebar(lux: Lux, prefix: String, guide: Guide, selectedHeading: String) {
|
|
112
|
+
lux.add("nav") {
|
|
113
|
+
lux.cssClass(Styles.guideSidebarCss)
|
|
114
|
+
lux.add("form") {
|
|
115
|
+
lux.set("role", "search")
|
|
116
|
+
lux.add("input") {
|
|
117
|
+
lux.set("aria-label", "Search")
|
|
118
|
+
lux.cssClass(Styles.searchInputCss)
|
|
119
|
+
lux.set("placeholder", "Search...")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
lux.add("ul") {
|
|
123
|
+
lux.cssClass(Styles.guideSidebarUlCss)
|
|
124
|
+
mutable first = True
|
|
125
|
+
guide.documents.each {document =>
|
|
126
|
+
lux.add("li") {
|
|
127
|
+
lux.cssClass(Styles.guideSidebarLiCss)
|
|
128
|
+
lux.add("a") {
|
|
129
|
+
let heading = document.sections.grabFirst().heading
|
|
130
|
+
lux.cssClass(Styles.whiteLinkCss)
|
|
131
|
+
if(heading == selectedHeading) {lux.set("aria-current", "page")}
|
|
132
|
+
lux.set("href", if(first) {prefix} else {prefix + kebabCase(heading)})
|
|
133
|
+
lux.text(heading)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
first = False
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
renderDocument(lux: Lux, document: Document, demos: List[Demo]) {
|
|
143
|
+
lux.add("article") {
|
|
144
|
+
lux.cssClass(Styles.guideDocumentCss)
|
|
145
|
+
document.sections.pairs().each {| Pair(index, section) =>
|
|
146
|
+
renderSection(lux, index == 0, section, demos)
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
renderSection(lux: Lux, first: Bool, section: Section, demos: List[Demo]) {
|
|
152
|
+
lux.add(if(first) {"h1"} else {"h2"}) {
|
|
153
|
+
lux.cssClass(if(first) {Styles.guideH1Css} else {Styles.guideH2Css})
|
|
154
|
+
lux.text(section.heading)
|
|
155
|
+
}
|
|
156
|
+
section.blocks.each {renderBlock(lux, _, demos)}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
renderBlock(lux: Lux, block: Block, demos: List[Demo]) {
|
|
160
|
+
block.{
|
|
161
|
+
| Paragraph(inlines) => renderParagraph(lux, inlines)
|
|
162
|
+
| CodeBlock(code, firefly) => renderCodeBlock(lux, code, firefly)
|
|
163
|
+
| Bullets(items) => renderBullets(lux, items)
|
|
164
|
+
| Image(url) => renderImage(lux, url)
|
|
165
|
+
| Video(url) => renderVideo(lux, url)
|
|
166
|
+
| LuxDemo(name) => renderLuxDemo(lux, name, demos)
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
renderParagraph(lux: Lux, inlines: List[Inline]) {
|
|
171
|
+
lux.add("p") {
|
|
172
|
+
renderInlines(lux, inlines)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
renderCodeBlock(lux: Lux, code: String, firefly: Bool) {
|
|
177
|
+
let lines = code.lines().dropWhile {_.all {_ == ' '}}.reverse().dropWhile {_.all {_ == ' '}}.reverse()
|
|
178
|
+
let indentation = lines.first().map {_.takeWhile {_ == ' '}.size()}.else {0}
|
|
179
|
+
lux.add("pre") {
|
|
180
|
+
lux.cssClass(Styles.guideCodeBlockCss)
|
|
181
|
+
lux.add("code") {
|
|
182
|
+
lux.cssClass(Styles.guideCodeCss)
|
|
183
|
+
let unindentedCode = lines.map {_.dropFirst(indentation)}.join("\n")
|
|
184
|
+
if(firefly) {
|
|
185
|
+
renderHighlightedCode(lux, unindentedCode)
|
|
186
|
+
} else {
|
|
187
|
+
lux.text(unindentedCode)
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
renderBullets(lux: Lux, items: List[List[Inline]]) {
|
|
194
|
+
lux.add("ul") {
|
|
195
|
+
items.each {inlines =>
|
|
196
|
+
lux.add("li") {
|
|
197
|
+
renderInlines(lux, inlines)
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
renderImage(lux: Lux, url: String) {
|
|
204
|
+
lux.add("img") {
|
|
205
|
+
lux.set("src", url)
|
|
206
|
+
lux.css(Css.maxWidth("100%"))
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
renderVideo(lux: Lux, url: String) {
|
|
211
|
+
lux.add("video") {
|
|
212
|
+
lux.set("src", url)
|
|
213
|
+
lux.css(Css.maxWidth("100%"))
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
renderLuxDemo(lux: Lux, name: String, demos: List[Demo]) {
|
|
218
|
+
demos.find {_.name == name}.map {_.render(lux)}.else {lux.text("Demo not found")}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
renderInlines(lux: Lux, inlines: List[Inline]) {
|
|
222
|
+
inlines.separate([Text(" ")]).each {renderInline(lux, _)}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
renderInline(lux: Lux, inline: Inline) {
|
|
226
|
+
inline.{
|
|
227
|
+
| Anchor(heading, title) =>
|
|
228
|
+
lux.add("a") {
|
|
229
|
+
let url = title.map {"/guide/" + kebabCase(_)}.else {""} + "#" + kebabCase(heading)
|
|
230
|
+
lux.cssClass(Styles.guideLinkCss)
|
|
231
|
+
lux.set("href", url)
|
|
232
|
+
lux.text(title.map {_ + ": "}.else {""} + heading)
|
|
233
|
+
}
|
|
234
|
+
| Bold(text) =>
|
|
235
|
+
lux.add("b") {
|
|
236
|
+
lux.text(text)
|
|
237
|
+
}
|
|
238
|
+
| Code(code, firefly) =>
|
|
239
|
+
lux.add("code") {
|
|
240
|
+
lux.cssClass(Styles.guideCodeCss)
|
|
241
|
+
if(firefly) {
|
|
242
|
+
renderHighlightedCode(lux, code)
|
|
243
|
+
} else {
|
|
244
|
+
lux.text(code)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
| Italic(text) =>
|
|
248
|
+
lux.add("i") {
|
|
249
|
+
lux.text(text)
|
|
250
|
+
}
|
|
251
|
+
| Link(text, url) =>
|
|
252
|
+
lux.add("a") {
|
|
253
|
+
lux.cssClass(Styles.guideLinkCss)
|
|
254
|
+
lux.set("href", url)
|
|
255
|
+
lux.text(text)
|
|
256
|
+
}
|
|
257
|
+
| Text(text) =>
|
|
258
|
+
lux.text(text)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
renderHighlightedCode(lux: Lux, code: String) {
|
|
263
|
+
let tokens = Tokenizer.tokenize("<example>", code, None, False)
|
|
264
|
+
mutable offset = 0
|
|
265
|
+
mutable index = 0
|
|
266
|
+
tokens.each {token =>
|
|
267
|
+
if(token.startOffset > offset) {
|
|
268
|
+
lux.span {
|
|
269
|
+
lux.cssClass(Styles.codeCommentCss)
|
|
270
|
+
lux.text(code.slice(offset, token.startOffset))
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
if(token.kind != LEnd) {
|
|
274
|
+
let css = token.kind.{
|
|
275
|
+
| LChar => Styles.codeStringCss
|
|
276
|
+
| LFloat => Styles.codeNumberCss
|
|
277
|
+
| LInt => Styles.codeNumberCss
|
|
278
|
+
| LKeyword => Styles.codeKeywordCss
|
|
279
|
+
| LNamespace => Styles.codeTypeCss
|
|
280
|
+
| LString => Styles.codeStringCss
|
|
281
|
+
| LUpper => Styles.codeTypeCss
|
|
282
|
+
| LWildcard => Styles.codeVariableCss
|
|
283
|
+
| LLower {tokens.get(index - 1).any {t =>
|
|
284
|
+
t.kind == LBracketRight || t.kind == LDot
|
|
285
|
+
}} => Styles.codeCallCss
|
|
286
|
+
| LLower {tokens.grab(index + 1).kind == LBracketLeft} => Styles.codeCallCss
|
|
287
|
+
| LLower => Styles.codeVariableCss
|
|
288
|
+
| _ => Styles.codeOtherCss
|
|
289
|
+
}
|
|
290
|
+
lux.span {
|
|
291
|
+
lux.cssClass(css)
|
|
292
|
+
lux.text(code.slice(token.startOffset, token.stopOffset))
|
|
293
|
+
}
|
|
294
|
+
offset = token.stopOffset
|
|
295
|
+
}
|
|
296
|
+
index += 1
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
kebabCase(text: String): String {
|
|
301
|
+
mutable result = text.trim().lower().filter {c => c.isAsciiLetterOrDigit() || c == ' '}
|
|
302
|
+
doWhile {
|
|
303
|
+
let before = result
|
|
304
|
+
result = result.replace(" ", "")
|
|
305
|
+
result != before
|
|
306
|
+
}
|
|
307
|
+
result.replace(" ", "-")
|
|
308
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
|
|
3
|
+
mock(): List[Document] {
|
|
4
|
+
[
|
|
5
|
+
new()
|
|
6
|
+
Document([Section("Control flow", [])])
|
|
7
|
+
Document([Section("Base types", [])])
|
|
8
|
+
Document([Section("Collections", [])])
|
|
9
|
+
Document([Section("Custom types", [])])
|
|
10
|
+
Document([Section("Async I/O", [])])
|
|
11
|
+
Document([Section("Structured concurrency", [])])
|
|
12
|
+
Document([Section("Applications", [])])
|
|
13
|
+
Document([Section("Packages and modules", [])])
|
|
14
|
+
Document([Section("Functions and methods", [])])
|
|
15
|
+
Document([Section("Traits and instances", [])])
|
|
16
|
+
Document([Section("JavaScript interop", [])])
|
|
17
|
+
]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
new(): Document {
|
|
21
|
+
Document([
|
|
22
|
+
Section("Introduction", [
|
|
23
|
+
Paragraph([
|
|
24
|
+
Text("In this guide you will learn to use Firefly.")
|
|
25
|
+
Text("Firefly is a programming language that compiles to JavaScript.")
|
|
26
|
+
Text("It runs in the browser and on the server, and lets you develop webapps with a minimum of hassle.")
|
|
27
|
+
])
|
|
28
|
+
])
|
|
29
|
+
Section("Getting started", [
|
|
30
|
+
Paragraph([
|
|
31
|
+
Text("Firefly comes with a compiler, a build system and a package manager.")
|
|
32
|
+
])
|
|
33
|
+
Paragraph([
|
|
34
|
+
Text("Install Firefly:")
|
|
35
|
+
])
|
|
36
|
+
CodeBlock("npm install -g firefly-compiler")
|
|
37
|
+
Paragraph([
|
|
38
|
+
Text("Save this code as")
|
|
39
|
+
Code("Hello.ff")
|
|
40
|
+
Text(":")
|
|
41
|
+
])
|
|
42
|
+
CodeBlock("""
|
|
43
|
+
nodeMain(system: NodeSystem) {
|
|
44
|
+
system.writeLine("Hello, World!")
|
|
45
|
+
}
|
|
46
|
+
""", firefly = True)
|
|
47
|
+
Paragraph([
|
|
48
|
+
Text("And run it:")
|
|
49
|
+
])
|
|
50
|
+
CodeBlock("firefly Hello.ff")
|
|
51
|
+
Paragraph([
|
|
52
|
+
Text("You now know how to run Firefly code!")
|
|
53
|
+
])
|
|
54
|
+
])
|
|
55
|
+
Section("Editor support", [
|
|
56
|
+
Paragraph([
|
|
57
|
+
Text("Install the")
|
|
58
|
+
Link("Firefly Language VSCode extension", "https://marketplace.visualstudio.com/items?itemName=firefly-team.firefly-lang")
|
|
59
|
+
Text(" to get autocompletion, renaming, go to definition, highlighting, etc. You can find it under Extensions in VSCode.")
|
|
60
|
+
])
|
|
61
|
+
Paragraph([
|
|
62
|
+
Text("The Firefly code highlighting on this site matches the Dark Modern theme in VSCode.")
|
|
63
|
+
])
|
|
64
|
+
Paragraph([
|
|
65
|
+
Text("If you'd like to make an extension for a different editor, the language server is available, as well as the source code for the VSCode extension.")
|
|
66
|
+
])
|
|
67
|
+
])
|
|
68
|
+
])
|
|
69
|
+
}
|
package/fireflysite/Main.ff
CHANGED
|
@@ -1,15 +1,92 @@
|
|
|
1
|
-
dependency ff:webserver:0.0.0
|
|
2
1
|
import WebServer from ff:webserver
|
|
2
|
+
import Lux from ff:lux
|
|
3
|
+
import Guide
|
|
4
|
+
import GuideIntroduction
|
|
5
|
+
import ExamplesOverview
|
|
6
|
+
import PackagesOverview
|
|
7
|
+
import CommunityOverview
|
|
8
|
+
import Styles
|
|
9
|
+
import CountingButtonDemo
|
|
10
|
+
import MatchingPasswordsDemo
|
|
3
11
|
|
|
4
12
|
nodeMain(system: NodeSystem): Unit {
|
|
5
13
|
let host = system.arguments().grab(0)
|
|
6
14
|
let port = system.arguments().grab(1).grabInt()
|
|
7
15
|
WebServer.new(system, host, port).listen {request =>
|
|
8
|
-
let
|
|
9
|
-
|
|
16
|
+
let path = request.readPath()
|
|
17
|
+
if(path.startsWith("/guide/")) {
|
|
18
|
+
serveGuideHtml("Firefly Guide", request)
|
|
19
|
+
} elseIf {path.startsWith("/examples/")} {
|
|
20
|
+
serveGuideHtml("Firefly Examples", request)
|
|
21
|
+
} elseIf {path.startsWith("/packages/")} {
|
|
22
|
+
serveGuideHtml("Firefly Packages", request)
|
|
23
|
+
} elseIf {path.startsWith("/community/")} {
|
|
24
|
+
serveGuideHtml("Firefly Community", request)
|
|
25
|
+
} elseIf {path.startsWith("/js/") && !path.contains("..")} {
|
|
26
|
+
request.writeHeader("Content-Type", "text/javascript; charset=UTF-8")
|
|
27
|
+
let asset = if(path == "/js/ff/fireflysite/Main.mjs" && system.assets().exists("/js/Main.bundle.js")) {
|
|
28
|
+
"/js/Main.bundle.js"
|
|
29
|
+
} else {
|
|
30
|
+
path
|
|
31
|
+
}
|
|
32
|
+
request.writeText(system.assets().readText(asset))
|
|
33
|
+
} elseIf {path.startsWith("/favicon.ico")} {
|
|
34
|
+
request.writeHeader("Content-Type", "image/png")
|
|
35
|
+
request.writeStream(system.assets().readStream("/images/firefly-logo-notext.png"))
|
|
36
|
+
} else {
|
|
37
|
+
let parameters = if(request.readRawQueryString().size() == 0) {""} else {
|
|
38
|
+
"?" + request.readRawQueryString()
|
|
39
|
+
}
|
|
40
|
+
request.writeHeader("Location", "https://www.firefly-lang.org" + path + parameters)
|
|
41
|
+
request.writeStatus("302 Found")
|
|
10
42
|
}
|
|
11
|
-
request.writeHeader("Location", "https://www.firefly-lang.org" + request.readPath() + parameters)
|
|
12
|
-
request.writeStatus("302 Found")
|
|
13
43
|
}
|
|
14
44
|
}
|
|
15
45
|
|
|
46
|
+
browserMain(system: BrowserSystem): Unit {
|
|
47
|
+
if(system.urlPath().startsWith("/guide/")) {
|
|
48
|
+
let kebab = system.urlPath().dropFirst("/guide/".size())
|
|
49
|
+
Lux.renderById(system, "main") {lux =>
|
|
50
|
+
Guide.render(lux, "/guide/", kebab, Guide(GuideIntroduction.mock()), [])
|
|
51
|
+
}
|
|
52
|
+
} elseIf {system.urlPath().startsWith("/examples/")} {
|
|
53
|
+
let kebab = system.urlPath().dropFirst("/examples/".size())
|
|
54
|
+
let demos = [CountingButtonDemo.new(), MatchingPasswordsDemo.new()]
|
|
55
|
+
Lux.renderById(system, "main") {lux =>
|
|
56
|
+
Guide.render(lux, "/examples/", kebab, Guide(ExamplesOverview.mock()), demos)
|
|
57
|
+
}
|
|
58
|
+
} elseIf {system.urlPath().startsWith("/packages/")} {
|
|
59
|
+
let kebab = system.urlPath().dropFirst("/packages/".size())
|
|
60
|
+
Lux.renderById(system, "main") {lux =>
|
|
61
|
+
Guide.renderSoleDocument(lux, "/packages/", PackagesOverview.new(), [])
|
|
62
|
+
}
|
|
63
|
+
} else {
|
|
64
|
+
let kebab = system.urlPath().dropFirst("/community/".size())
|
|
65
|
+
Lux.renderById(system, "main") {lux =>
|
|
66
|
+
Guide.renderSoleDocument(lux, "/community/", CommunityOverview.new(), [])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
buildMain(system: BuildSystem) {
|
|
72
|
+
let browser = system.compileForBrowser("Main.ff")
|
|
73
|
+
let assets = AssetSystem.create()
|
|
74
|
+
.addAssets("/js/", browser.assets())
|
|
75
|
+
.addAssets("/images/", system.packageAssets().asset("/firefly-logo-notext.png"))
|
|
76
|
+
system.setAssets(assets)
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
serveGuideHtml(title: String, request: WebRequest[WebResponse]): Unit {
|
|
80
|
+
request.writeHeader("Content-Type", "text/html; charset=UTF-8")
|
|
81
|
+
request.writeText("<!doctype html>")
|
|
82
|
+
request.writeText("<html lang='en' style='background-color: #303236; color: #dadada; width: 100%; height: 100%; color-scheme: dark;'>")
|
|
83
|
+
request.writeText("<head>")
|
|
84
|
+
request.writeText("<title>" + title + "</title>")
|
|
85
|
+
request.writeText("<meta name='viewport' content='width=device-width, initial-scale=1.0'>")
|
|
86
|
+
request.writeText("</head>")
|
|
87
|
+
request.writeText("<body style='margin: 0; padding: 0; width: 100%; height: 100%'>")
|
|
88
|
+
request.writeText("<div id='main'></div>")
|
|
89
|
+
request.writeText("<script type='module' src='/js/ff/fireflysite/Main.mjs'></script>")
|
|
90
|
+
request.writeText("</body>")
|
|
91
|
+
request.writeText("</html>")
|
|
92
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
import Lux from ff:lux
|
|
3
|
+
import LuxEvent from ff:lux
|
|
4
|
+
|
|
5
|
+
name = "Matching passwords"
|
|
6
|
+
|
|
7
|
+
new(): Demo {
|
|
8
|
+
Demo(
|
|
9
|
+
name
|
|
10
|
+
{render(_)}
|
|
11
|
+
)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
render(lux: Lux): Unit {
|
|
15
|
+
function passwordInput(password: String, setPassword: String => Unit) {
|
|
16
|
+
lux.div {
|
|
17
|
+
lux.input {
|
|
18
|
+
lux.set("type", "password")
|
|
19
|
+
lux.set("autocomplete", "new-password")
|
|
20
|
+
lux.setValue(password)
|
|
21
|
+
lux.onInput {event =>
|
|
22
|
+
setPassword(event.text())
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
lux.form {
|
|
28
|
+
lux.useState(""): password1, setPassword1 =>
|
|
29
|
+
lux.useState(""): password2, setPassword2 =>
|
|
30
|
+
passwordInput(password1, setPassword1)
|
|
31
|
+
passwordInput(password2, setPassword2)
|
|
32
|
+
if(password2.size() != 0) {
|
|
33
|
+
lux.text(
|
|
34
|
+
if(password1 == password2) {
|
|
35
|
+
"Passwords match!"
|
|
36
|
+
} else {
|
|
37
|
+
"Passwords don't match."
|
|
38
|
+
}
|
|
39
|
+
)
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
newDocument(): Document {
|
|
45
|
+
Document([
|
|
46
|
+
Section(name, [
|
|
47
|
+
Paragraph([Text("Two passwords fields and a check that they match.")])
|
|
48
|
+
])
|
|
49
|
+
Section("Demo", [
|
|
50
|
+
LuxDemo(name)
|
|
51
|
+
])
|
|
52
|
+
Section("Code", [
|
|
53
|
+
CodeBlock("""
|
|
54
|
+
render(lux: Lux): Unit {
|
|
55
|
+
function passwordInput(password: String, setPassword: String => Unit) {
|
|
56
|
+
lux.div {
|
|
57
|
+
lux.input {
|
|
58
|
+
lux.set("type", "password")
|
|
59
|
+
lux.set("autocomplete", "new-password")
|
|
60
|
+
lux.setValue(password)
|
|
61
|
+
lux.onInput {event =>
|
|
62
|
+
setPassword(event.text())
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
lux.form {
|
|
68
|
+
lux.useState(""): password1, setPassword1 =>
|
|
69
|
+
lux.useState(""): password2, setPassword2 =>
|
|
70
|
+
passwordInput(password1, setPassword1)
|
|
71
|
+
passwordInput(password2, setPassword2)
|
|
72
|
+
if(password2.size() != 0) {
|
|
73
|
+
lux.text(
|
|
74
|
+
if(password1 == password2) {
|
|
75
|
+
"Passwords match!"
|
|
76
|
+
} else {
|
|
77
|
+
"Passwords don't match."
|
|
78
|
+
}
|
|
79
|
+
)
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
""", firefly = True)
|
|
84
|
+
])
|
|
85
|
+
])
|
|
86
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import Guide
|
|
2
|
+
import CountingButtonDemo
|
|
3
|
+
import MatchingPasswordsDemo
|
|
4
|
+
|
|
5
|
+
new(): Document {
|
|
6
|
+
Document([
|
|
7
|
+
Section("Packages", [
|
|
8
|
+
Paragraph([
|
|
9
|
+
Text("Here you'll eventually find the package index.")
|
|
10
|
+
Text("For now, here's a short list of essential packages.")
|
|
11
|
+
])
|
|
12
|
+
])
|
|
13
|
+
Section("ff:core", [
|
|
14
|
+
Paragraph([
|
|
15
|
+
Text("The standard library of Firefly.")
|
|
16
|
+
])
|
|
17
|
+
])
|
|
18
|
+
Section("ff:webserver", [
|
|
19
|
+
Paragraph([
|
|
20
|
+
Text("A webserver package. Has HTTPS and WebSocket support.")
|
|
21
|
+
])
|
|
22
|
+
])
|
|
23
|
+
Section("ff:websocket", [
|
|
24
|
+
Paragraph([
|
|
25
|
+
Text("A package for making websocket connections.")
|
|
26
|
+
])
|
|
27
|
+
])
|
|
28
|
+
Section("ff:postgresql", [
|
|
29
|
+
Paragraph([
|
|
30
|
+
Text("A package for connecting to PostgreSQL. Has connection pool support.")
|
|
31
|
+
])
|
|
32
|
+
])
|
|
33
|
+
Section("ff:lux", [
|
|
34
|
+
Paragraph([
|
|
35
|
+
Text("A package for building interactive web interfaces.")
|
|
36
|
+
])
|
|
37
|
+
])
|
|
38
|
+
Section("ff:rpc", [
|
|
39
|
+
Paragraph([
|
|
40
|
+
Text("A package for type safe remote procedure calls.")
|
|
41
|
+
])
|
|
42
|
+
])
|
|
43
|
+
Section("ff:unsafejs", [
|
|
44
|
+
Paragraph([
|
|
45
|
+
Text("A FFI package for JavaScript.")
|
|
46
|
+
])
|
|
47
|
+
])
|
|
48
|
+
])
|
|
49
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
import Css from ff:lux
|
|
2
|
+
|
|
3
|
+
mobileMediaQuery = "@media only screen and (max-width: 600px)"
|
|
4
|
+
desktopMediaQuery = "@media only screen and (min-width: 1500px)"
|
|
5
|
+
|
|
6
|
+
pageCss: CssClass = CssClass(
|
|
7
|
+
[
|
|
8
|
+
Css.display("flex")
|
|
9
|
+
Css.flexDirection("column")
|
|
10
|
+
Css.minHeight("100vh")
|
|
11
|
+
Css.fontFamily("'Helvetica Neue', Helvetica, Arial, sans-serif")
|
|
12
|
+
Css.textRendering("optimizeLegibility")
|
|
13
|
+
]
|
|
14
|
+
[
|
|
15
|
+
CssNest("*:focus-visible", [
|
|
16
|
+
Css.outline("2px solid #4fc1ff")
|
|
17
|
+
Css.outlineOffset("2px")
|
|
18
|
+
], [])
|
|
19
|
+
]
|
|
20
|
+
[]
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
whiteLinkCss: CssClass = CssClass(
|
|
24
|
+
[
|
|
25
|
+
Css.color("#dadada")
|
|
26
|
+
Css.textDecoration("none")
|
|
27
|
+
]
|
|
28
|
+
[
|
|
29
|
+
CssNest("&:hover", [Css.textDecoration("underline")], [])
|
|
30
|
+
CssNest("&[aria-current='page']", [Css.color("#4ec9b0")], [])
|
|
31
|
+
]
|
|
32
|
+
[]
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
greenLinkCss: CssClass = CssClass(
|
|
36
|
+
[
|
|
37
|
+
Css.color("#4ec9b0")
|
|
38
|
+
Css.textDecoration("none")
|
|
39
|
+
]
|
|
40
|
+
[
|
|
41
|
+
CssNest("&:hover", [Css.textDecoration("underline")], [])
|
|
42
|
+
]
|
|
43
|
+
[]
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
topbarCss: CssClass = CssClass(
|
|
47
|
+
[
|
|
48
|
+
Css.display("flex")
|
|
49
|
+
Css.gap("30px")
|
|
50
|
+
Css.paddingLeft("20px")
|
|
51
|
+
Css.paddingRight("30px")
|
|
52
|
+
Css.lineHeight("49px")
|
|
53
|
+
Css.fontSize("17px")
|
|
54
|
+
Css.backgroundColor("#34373d")
|
|
55
|
+
Css.borderBottom("1px solid #2b2d30")
|
|
56
|
+
Css.boxSizing("border-box")
|
|
57
|
+
]
|
|
58
|
+
[
|
|
59
|
+
CssNest(mobileMediaQuery, [
|
|
60
|
+
Css.paddingLeft("20px")
|
|
61
|
+
Css.paddingRight("20px")
|
|
62
|
+
Css.gap("10px")
|
|
63
|
+
Css.fontSize("15px")
|
|
64
|
+
Css.overflowX("auto")
|
|
65
|
+
Css.justifyContent("space-between")
|
|
66
|
+
], [])
|
|
67
|
+
]
|
|
68
|
+
[]
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
topbarFireflyCss: CssClass = CssClass(
|
|
72
|
+
[
|
|
73
|
+
Css.marginRight("auto")
|
|
74
|
+
Css.fontSize("19px")
|
|
75
|
+
]
|
|
76
|
+
[
|
|
77
|
+
CssNest(mobileMediaQuery, [
|
|
78
|
+
Css.fontSize("15px")
|
|
79
|
+
], [])
|
|
80
|
+
]
|
|
81
|
+
[]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
searchInputCss: CssClass = CssClass(
|
|
85
|
+
[
|
|
86
|
+
Css.appearance("none")
|
|
87
|
+
Css.boxSizing("border-box")
|
|
88
|
+
Css.marginLeft("15px")
|
|
89
|
+
Css.marginRight("25px")
|
|
90
|
+
Css.width("calc(100% - 15px - 25px)")
|
|
91
|
+
Css.display("flex")
|
|
92
|
+
Css.border("none")
|
|
93
|
+
Css.backgroundColor("#34373d")
|
|
94
|
+
Css.color("#dadada")
|
|
95
|
+
Css.height("34px")
|
|
96
|
+
Css.fontSize("15px")
|
|
97
|
+
Css.borderRadius("17px")
|
|
98
|
+
Css.paddingLeft("15px")
|
|
99
|
+
Css.paddingRight("15px")
|
|
100
|
+
Css.paddingTop("0")
|
|
101
|
+
Css.paddingBottom("0")
|
|
102
|
+
Css.marginBottom("20px")
|
|
103
|
+
]
|
|
104
|
+
[
|
|
105
|
+
CssNest("&::placeholder", [
|
|
106
|
+
Css.color("#dadada")
|
|
107
|
+
Css.opacity("0.7")
|
|
108
|
+
], [])
|
|
109
|
+
]
|
|
110
|
+
[]
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
guideCss: CssClass = CssClass(
|
|
114
|
+
[
|
|
115
|
+
Css.display("flex")
|
|
116
|
+
]
|
|
117
|
+
[
|
|
118
|
+
CssNest(mobileMediaQuery, [
|
|
119
|
+
Css.flexDirection("column")
|
|
120
|
+
], [])
|
|
121
|
+
]
|
|
122
|
+
[]
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
guideSidebarCss: CssClass = CssClass(
|
|
126
|
+
[
|
|
127
|
+
Css.width("300px")
|
|
128
|
+
Css.minHeight("calc(100vh - 50px)")
|
|
129
|
+
Css.paddingTop("20px")
|
|
130
|
+
Css.backgroundColor("#2b2d30")
|
|
131
|
+
Css.boxSizing("border-box")
|
|
132
|
+
]
|
|
133
|
+
[
|
|
134
|
+
CssNest(mobileMediaQuery, [
|
|
135
|
+
Css.order("2")
|
|
136
|
+
Css.width("100%")
|
|
137
|
+
Css.height("auto")
|
|
138
|
+
Css.paddingTop("20px")
|
|
139
|
+
Css.paddingBottom("20px")
|
|
140
|
+
], [])
|
|
141
|
+
]
|
|
142
|
+
[]
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
guideSidebarUlCss: CssClass = CssClass(
|
|
146
|
+
[
|
|
147
|
+
Css.display("flex")
|
|
148
|
+
Css.flexDirection("column")
|
|
149
|
+
Css.gap("25px")
|
|
150
|
+
Css.listStyle("none")
|
|
151
|
+
Css.margin("0px")
|
|
152
|
+
Css.padding("20px")
|
|
153
|
+
]
|
|
154
|
+
[]
|
|
155
|
+
[]
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
guideSidebarLiCss: CssClass = CssClass(
|
|
159
|
+
[
|
|
160
|
+
Css.listStyle("none")
|
|
161
|
+
]
|
|
162
|
+
[]
|
|
163
|
+
[]
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
guideMainCss: CssClass = CssClass(
|
|
167
|
+
[
|
|
168
|
+
Css.flex("1")
|
|
169
|
+
Css.display("flex")
|
|
170
|
+
Css.justifyContent("center")
|
|
171
|
+
Css.padding("20px")
|
|
172
|
+
Css.paddingTop("70px")
|
|
173
|
+
Css.position("relative")
|
|
174
|
+
Css.boxSizing("content-box")
|
|
175
|
+
]
|
|
176
|
+
[
|
|
177
|
+
CssNest(mobileMediaQuery, [
|
|
178
|
+
Css.paddingTop("30px")
|
|
179
|
+
], [])
|
|
180
|
+
CssNest(desktopMediaQuery, [
|
|
181
|
+
Css.paddingRight("200px")
|
|
182
|
+
], [])
|
|
183
|
+
]
|
|
184
|
+
[]
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
guideSoleDocumentCss: CssClass = CssClass(
|
|
188
|
+
[
|
|
189
|
+
Css.flex("1")
|
|
190
|
+
Css.display("flex")
|
|
191
|
+
Css.justifyContent("center")
|
|
192
|
+
Css.padding("20px")
|
|
193
|
+
Css.paddingTop("70px")
|
|
194
|
+
Css.position("relative")
|
|
195
|
+
Css.boxSizing("content-box")
|
|
196
|
+
]
|
|
197
|
+
[
|
|
198
|
+
CssNest(mobileMediaQuery, [
|
|
199
|
+
Css.paddingTop("30px")
|
|
200
|
+
], [])
|
|
201
|
+
]
|
|
202
|
+
[]
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
guideDocumentCss: CssClass = CssClass(
|
|
206
|
+
[
|
|
207
|
+
Css.maxWidth("800px")
|
|
208
|
+
Css.width("100%")
|
|
209
|
+
Css.fontSize("17px")
|
|
210
|
+
Css.lineHeight("1.7")
|
|
211
|
+
]
|
|
212
|
+
[
|
|
213
|
+
CssNest(mobileMediaQuery, [
|
|
214
|
+
Css.fontSize("16px")
|
|
215
|
+
Css.lineHeight("1.6")
|
|
216
|
+
], [])
|
|
217
|
+
]
|
|
218
|
+
[]
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
guideH1Css: CssClass = CssClass(
|
|
222
|
+
[
|
|
223
|
+
Css.fontSize("32px")
|
|
224
|
+
Css.marginTop("0px")
|
|
225
|
+
]
|
|
226
|
+
[
|
|
227
|
+
CssNest(mobileMediaQuery, [
|
|
228
|
+
Css.fontSize("30px")
|
|
229
|
+
], [])
|
|
230
|
+
]
|
|
231
|
+
[]
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
guideH2Css: CssClass = CssClass(
|
|
235
|
+
[
|
|
236
|
+
Css.fontSize("28px")
|
|
237
|
+
Css.marginTop("40px")
|
|
238
|
+
]
|
|
239
|
+
[
|
|
240
|
+
CssNest(mobileMediaQuery, [
|
|
241
|
+
Css.fontSize("25px")
|
|
242
|
+
], [])
|
|
243
|
+
]
|
|
244
|
+
[]
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
guideCodeCss: CssClass = CssClass(
|
|
248
|
+
[
|
|
249
|
+
Css.fontFamily("Consolas, 'Liberation Mono', Menlo, Courier, monospace")
|
|
250
|
+
Css.fontSize("17px")
|
|
251
|
+
Css.lineHeight("1.4")
|
|
252
|
+
Css.color("#4ec9b0")
|
|
253
|
+
]
|
|
254
|
+
[
|
|
255
|
+
CssNest(mobileMediaQuery, [
|
|
256
|
+
Css.fontSize("16px")
|
|
257
|
+
], [])
|
|
258
|
+
]
|
|
259
|
+
[]
|
|
260
|
+
)
|
|
261
|
+
|
|
262
|
+
guideCodeBlockCss: CssClass = CssClass(
|
|
263
|
+
[
|
|
264
|
+
Css.lineHeight("1.4")
|
|
265
|
+
Css.backgroundColor("#2b2d30")
|
|
266
|
+
Css.padding("12px 18px")
|
|
267
|
+
Css.paddingBottom("14px")
|
|
268
|
+
Css.border("1px solid #232323")
|
|
269
|
+
Css.borderRadius("5px")
|
|
270
|
+
Css.boxSizing("border-box")
|
|
271
|
+
Css.overflowX("auto")
|
|
272
|
+
Css.maxWidth("100%")
|
|
273
|
+
]
|
|
274
|
+
[
|
|
275
|
+
CssNest(mobileMediaQuery, [
|
|
276
|
+
Css.maxWidth("calc(100% + 40px)")
|
|
277
|
+
Css.marginLeft("-20px")
|
|
278
|
+
Css.marginRight("-20px")
|
|
279
|
+
Css.borderRadius("0")
|
|
280
|
+
Css.borderLeft("none")
|
|
281
|
+
Css.borderRight("none")
|
|
282
|
+
Css.padding("12px 20px")
|
|
283
|
+
], [])
|
|
284
|
+
]
|
|
285
|
+
[]
|
|
286
|
+
)
|
|
287
|
+
|
|
288
|
+
guideLinkCss: CssClass = CssClass(
|
|
289
|
+
[
|
|
290
|
+
Css.color("#4fc1ff")
|
|
291
|
+
Css.textDecoration("none")
|
|
292
|
+
]
|
|
293
|
+
[
|
|
294
|
+
CssNest("&:hover", [Css.textDecoration("underline")], [])
|
|
295
|
+
]
|
|
296
|
+
[]
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
codeCommentCss: CssClass = CssClass([Css.color("#6a9955")], [], [])
|
|
300
|
+
codeStringCss: CssClass = CssClass([Css.color("#ce9178")], [], [])
|
|
301
|
+
codeNumberCss: CssClass = CssClass([Css.color("#b5cea8")], [], [])
|
|
302
|
+
codeKeywordCss: CssClass = CssClass([Css.color("#569cd6")], [], [])
|
|
303
|
+
codeTypeCss: CssClass = CssClass([Css.color("#4ec9b0")], [], [])
|
|
304
|
+
codeVariableCss: CssClass = CssClass([Css.color("#9cdcfe")], [], [])
|
|
305
|
+
codeCallCss: CssClass = CssClass([Css.color("#dcdcaa")], [], [])
|
|
306
|
+
codeOtherCss: CssClass = CssClass([Css.color("#cccccc")], [], [])
|
|
Binary file
|
package/lux/Main2.ff
CHANGED
|
@@ -92,9 +92,8 @@ rhymeComponent(lux: Lux, system: BrowserSystem) {
|
|
|
92
92
|
|
|
93
93
|
|
|
94
94
|
rhyme(system: BrowserSystem, text: String): String {
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
if(json.get(0).isNullOrUndefined()) {"?"} else {json.get(0).get("word").grabString()}
|
|
95
|
+
let json = system.httpClient().get("https://api.datamuse.com/words?rel_rhy=" + text, []) {_.readJson()}
|
|
96
|
+
json.index(0).field("word").getString().else {"?"}
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
browserMain(system: BrowserSystem): Unit {
|
|
@@ -452,7 +452,7 @@ export function Json_field(self_, key_) {
|
|
|
452
452
|
|
|
453
453
|
export function Json_index(self_, key_) {
|
|
454
454
|
|
|
455
|
-
return
|
|
455
|
+
return Array.isArray(self_) ? self_[key] ?? null : null;
|
|
456
456
|
|
|
457
457
|
}
|
|
458
458
|
|
|
@@ -471,7 +471,7 @@ export function Json_getField(self_, key_) {
|
|
|
471
471
|
|
|
472
472
|
export function Json_getIndex(self_, key_) {
|
|
473
473
|
|
|
474
|
-
return
|
|
474
|
+
return Array.isArray(self_) ? ff_core_Option.Some(self_[key_] ?? null) : ff_core_Option.None();
|
|
475
475
|
|
|
476
476
|
}
|
|
477
477
|
|
|
@@ -329,6 +329,16 @@ export function String_all(self_, body_) {
|
|
|
329
329
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
|
+
export function String_filter(self_, body_) {
|
|
333
|
+
|
|
334
|
+
const result = [];
|
|
335
|
+
for(let i = 0; i < self_.length; i++) {
|
|
336
|
+
if(body_(self_.charCodeAt(i))) result.push(self_.charAt(i));
|
|
337
|
+
}
|
|
338
|
+
return result.join("");
|
|
339
|
+
|
|
340
|
+
}
|
|
341
|
+
|
|
332
342
|
export function String_toBuffer(self_) {
|
|
333
343
|
|
|
334
344
|
const encoded = new TextEncoder().encode(self_)
|
|
@@ -510,6 +520,16 @@ export async function String_all$(self_, body_, $task) {
|
|
|
510
520
|
|
|
511
521
|
}
|
|
512
522
|
|
|
523
|
+
export async function String_filter$(self_, body_, $task) {
|
|
524
|
+
|
|
525
|
+
const result = [];
|
|
526
|
+
for(let i = 0; i < self_.length; i++) {
|
|
527
|
+
if(await body_(self_.charCodeAt(i))) result.push(self_.charAt(i));
|
|
528
|
+
}
|
|
529
|
+
return result.join("");
|
|
530
|
+
|
|
531
|
+
}
|
|
532
|
+
|
|
513
533
|
export async function String_toBuffer$(self_, $task) {
|
|
514
534
|
throw new Error('Function String_toBuffer is missing on this target in async context.');
|
|
515
535
|
}
|
package/package.json
CHANGED