lexgui 0.1.45 → 0.2.0

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/demo.js CHANGED
@@ -5,871 +5,121 @@ import 'lexgui/components/audio.js';
5
5
 
6
6
  window.LX = LX;
7
7
 
8
- // Init library and get main area
9
- let area = LX.init();
8
+ let area = LX.init( { strictViewport: false } );
10
9
 
11
- // Change global properties after init
12
- // LX.DEFAULT_NAME_WIDTH = "10%";
13
- // LX.DEFAULT_SPLITBAR_SIZE = 16;
14
- // LX.OPEN_CONTEXTMENU_ENTRY = 'mouseover';
15
-
16
- // LX.message("Im in another position", null, { position: [10, 10] });
17
- // LX.message("Welcome to Lexgui", "Welcome!");
18
-
19
- // Change some theme colors...
20
- // LX.setThemeColor('global-color-primary', "#211");
21
- // LX.setThemeColor('global-selected', "#a74");
22
- // LX.setThemeColor('global-text', "#f21");
23
-
24
- // menu bar
25
- area.addMenubar( m => {
26
-
27
- // {options}: callback, icon, short
28
-
29
- m.add( "Scene/New Scene", () => { console.log("New scene created!") });
30
- m.add( "Scene/Open Scene", { icon: "fa-solid fa-folder-open", short: "S", callback: () => { console.log("Opening SCENE Dialog") } } );
31
- m.add( "Scene/Open Recent/hello.scene", name => { console.log("Opening " + name) });
32
- m.add( "Scene/Open Recent/goodbye.scene", name => { console.log("Opening " + name) });
33
- m.add( "Project/Project Settings" );
34
- m.add( "Project/Export", { icon: "fa-solid fa-download" });
35
- m.add( "Project/Export/DAE", { icon: "fa-solid fa-cube", short: "D", callback: () => { console.log("Exporting DAE...") }} );
36
- m.add( "Project/Export/GLTF", { short: "G" } );
37
- m.add( "Editor/Autosave", { type: 'checkbox', icon: "fa fa-floppy-disk", callback: (v, name) => { console.log(name + ": " + v ) } });
38
- m.add( "Editor/Test", () => LX.prompt("Write in the text area below the bml instructions to move the avatar from the web application. A sample of BML instructions can be tested through the helper tabs in the right panel.", "Test?"));
39
- m.add( "Editor/Settings", { icon: "fa-solid fa-gears", callback: () => {
40
- const dialog = new LX.Dialog( "Settings", p => {
41
- p.addText("A Text", "Testing first widget");
42
- p.sameLine(3);
43
- p.addLabel("Buttons:");
44
- p.addButton(null, "Click me", () => {
45
- console.log( p.getValue("A Text") );
46
- });
47
- p.addButton(null, "Click me v2!", () => {
48
- console.log( p.getValue("A Text") );
49
- });
50
- });
51
- }} );
52
- m.add( "Editor/Write BML", { icon: "fa-solid fa-gears", callback: () => {
53
-
54
- new LX.PocketDialog( "BML Instruction", p => {
55
-
56
- let htmlStr = "Write in the text area below the bml instructions to move the avatar from the web application. A sample of BML instructions can be tested through the helper tabs in the right panel.";
57
- p.addTextArea(null, htmlStr, null, {disabled: true, fitHeight: true});
58
-
59
- p.addButton(null, "Click here to see BML instructions and attributes", () => {
60
- window.open("https://github.com/upf-gti/SignON-realizer/blob/SiGMLExperiments/docs/InstructionsBML.md");
61
- });
62
-
63
- htmlStr = "Note: In 'speech', all text between '%' is treated as actual words. An automatic translation from words (dutch) to phonemes (arpabet) is performed.";
64
- htmlStr += "\n\nNote: Each instruction is inside '{}'. Each instruction is separated by a coma ',' except que last one.";
65
- p.addTextArea(null, htmlStr, null, {disabled: true, fitHeight: true});
66
-
67
- htmlStr = 'An example: { "type":"speech", "start": 0, "text": "%hallo%.", "sentT": 1, "sentInt": 0.5 }, { "type": "gesture", "start": 0, "attackPeak": 0.5, "relax": 1, "end": 2, "locationBodyArm": "shoulder", "lrSym": true, "hand": "both", "distance": 0.1 }';
68
- p.addTextArea(null, htmlStr, null, {disabled: true, fitHeight: true});
69
-
70
- const area = new LX.Area({ height: "250px" });
71
- p.attach( area.root );
72
-
73
- window.editor = new LX.CodeEditor(area, {
74
- highlight: 'JSON',
75
- skip_info: true
76
- });
77
-
78
- p.addButton(null, "Send", () => {
79
- console.log(":)")
80
- });
81
-
82
- }, { size: ["30%", null], float: "right", draggable: false});
83
- }} );
84
- m.add( "Editor/Open AssetView", { icon: "fa-solid fa-rect", callback: () => { createAssetDialog(); }} );
85
- m.add( "Account/Login", { icon: "fa-solid fa-user", callback: () => { createLoginForm(); }} );
86
- m.add( "Timeline/Shortcuts", { disabled: true });
87
- m.add( "Timeline/Shortcuts/Play-Pause", { short: "SPACE" });
88
- m.add( "Timeline/Shortcuts/Zoom", { short: "Wheel" });
89
- m.add( "Timeline/Shortcuts/Change time", { short: "Left Click+Drag" });
90
- m.add( "Timeline/Shortcuts/Move keys", { short: "Hold CTRL" });
91
- m.add( "Timeline/Shortcuts/Add keys", { short: "Right Click" });
92
- m.add( "Timeline/Shortcuts/Delete keys");
93
- m.add( "Timeline/Shortcuts/Delete keys/Single", { short: "DEL" });
94
- m.add( "Timeline/Shortcuts/Delete keys/Multiple", { short: "Hold LSHIFT" });
95
- m.add( "Timeline/Shortcuts/Key Selection");
96
- m.add( "Timeline/Shortcuts/Key Selection/Single", { short: "Left Click" });
97
- m.add( "Timeline/Shortcuts/Key Selection/Multiple", { short: "Hold LSHIFT" });
98
- m.add( "Timeline/Shortcuts/Key Selection/Box", { short: "Hold LSHIFT+Drag" });
99
- m.add( "Help/Search Help", { icon: "fa-solid fa-magnifying-glass", short: "F1", callback: () => { window.open("./docs/") }});
100
- m.add( "Help/Support LexGUI/Please", { icon: "fa-solid fa-heart" } );
101
- m.add( "Help/Support LexGUI/Do it" );
102
- m.addButtons( [
103
- {
104
- title: "Play",
105
- icon: "fa-solid fa-play",
106
- swap: "fa-solid fa-stop",
107
- callback: (event, swapValue) => {
108
- if( swapValue ) console.log("play!");
109
- else console.log("stop!");
110
- }
111
- },
112
- {
113
- title: "Pause",
114
- icon: "fa-solid fa-pause",
115
- disabled: true,
116
- callback: (event) => { console.log("pause!"); }
117
- },
118
- {
119
- icon: "fa-solid fa-magnifying-glass",
120
- callback: (event) => { console.log("glass!"); }
121
- },
122
- {
123
- title: "Change Theme",
124
- icon: "fa-solid fa-moon",
125
- swap: "fa-solid fa-sun",
126
- callback: (event, swapValue) => { LX.setTheme( swapValue ? "light" : "dark" ) }
127
- }
128
- ]);
129
-
130
- m.getButton("Play");
131
- m.setButtonIcon("Github", "fa-brands fa-github", () => {window.open("https://github.com/jxarco/lexgui.js/")})
132
- m.setButtonImage("lexgui.js", "images/icon.png", () => {window.open("https://jxarco.github.io/lexgui.js/")}, {float: "left"})
133
- });
134
-
135
- // split main area
136
- var [left, right] = area.split({ sizes:["80%","20%"], minimizable: true });
137
-
138
- // left.addSidebar( m => {
139
- // m.add( "Scene", { icon: "fa fa-cube", callback: () => { } } );
140
- // m.add( "Code", { icon: "fa fa-code", callback: () => { } } );
141
- // m.add( "Search", { icon: "fa fa-search", bottom: true, callback: () => { } } );
142
- // });
143
-
144
- // split left area
145
- var [up, bottom] = left.split({ type: 'vertical', sizes:["50%", null], minimizable: true });
146
-
147
- var kfTimeline = null;
148
- var clipsTimeline = null;
149
- var curvesTimeline = null;
150
-
151
- bottom.onresize = bounding => {
152
- if(kfTimeline) kfTimeline.resize( [ bounding.width, bounding.height ] );
153
- if(clipsTimeline) clipsTimeline.resize( [ bounding.width, bounding.height ] );
154
- if(curvesTimeline) curvesTimeline.resize( [ bounding.width, bounding.height ] );
155
- }
156
-
157
- // another menu bar
158
- bottom.addMenubar( m => {
159
- m.add( "Information", e => {
160
- console.log(e);
161
- var el = document.getElementById('kf-timeline');
162
- if(el)
163
- el.style.display = 'none';
164
- el = document.getElementById('clips-timeline');
165
- if(el)
166
- el.style.display = 'none';
167
- el = document.getElementById('curves-timeline');
168
- if(el)
169
- el.style.display = 'none';
170
- var bottomPanel = document.getElementById('bottom-panel');
171
- bottomPanel.style.display = 'block';
172
- });
173
-
174
- m.add( "Keyframes Timeline", e => {
175
- console.log(e);
176
- let el = document.getElementById('bottom-panel');
177
- if(el)
178
- el.style.display = 'none';
179
- el = document.getElementById('clips-timeline');
180
- if(el)
181
- el.style.display = 'none';
182
- el = document.getElementById('curves-timeline');
183
- if(el)
184
- el.style.display = 'none';
185
- var timeline = document.getElementById('kf-timeline');
186
- if(timeline) {
187
- timeline.style.display = 'block';
188
- kfTimeline.resize();
189
- }
190
- else {
191
- kfTimeline = new LX.KeyFramesTimeline("kf-timeline", {
192
- onBeforeCreateTopBar: panel => {
193
- panel.addButton('', '<i class="fa fa-wand-magic-sparkles"></i>', ( value, event ) => { });
194
- }
195
- });
196
-
197
- bottom.attach(kfTimeline.root);
198
- kfTimeline.setSelectedItems(["Item 1", "Item 2", "Item 3"]);
199
- kfTimeline.setAnimationClip({tracks: [{name: "Item 1.position", values: [0,1,0, 1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1, 0.2, 0.3, 0.8]}, {name: "Item 3.position", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 3.scale", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}], duration: 1});
200
- kfTimeline.draw( 0 );
201
- }
202
- });
203
-
204
- m.add( "Clips Timeline", e => {
205
- console.log(e);
206
- let el = document.getElementById('bottom-panel');
207
- if(el)
208
- el.style.display = 'none';
209
-
210
- el = document.getElementById('kf-timeline');
211
- if(el)
212
- el.style.display = 'none';
213
- el = document.getElementById('curves-timeline');
214
- if(el)
215
- el.style.display = 'none';
216
- var ctimeline = document.getElementById('clips-timeline');
217
- if(ctimeline) {
218
- ctimeline.style.display = 'block';
219
- clipsTimeline.resize();
220
- }
221
- else {
222
- clipsTimeline = new LX.ClipsTimeline("clips-timeline", {width: m.root.clientWidth, height: m.parent.root.parentElement.clientHeight - m.root.clientHeight});
223
- bottom.attach(clipsTimeline.root);
224
- var clip = {id:"Clip1", start:0, duration:1, type:""};
225
- clipsTimeline.addClip(clip);
226
- var clip = {id:"Clip2", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
227
- clipsTimeline.addClip(clip);
228
- var clip = {id:"Clip3", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
229
- clipsTimeline.addClip(clip);
230
- var clip = {id:"Clip4", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
231
- clipsTimeline.addClip(clip);
232
- var clip = {id:"Clip5", start:0, fadein: 0.5, fadeout: 0.8, duration:1, type:""};
233
- clipsTimeline.addClip(clip);
234
-
235
- // clipsTimeline.setAnimationClip({tracks: [{clips: [clip]}], duration: 2});
236
- clipsTimeline.selectedItems = ["Clip1"];
237
-
238
- clipsTimeline.draw(0);
239
- }
240
- });
241
-
242
- m.add( "Curves Timeline", e => {
243
- console.log(e);
244
- let el = document.getElementById('bottom-panel');
245
- if(el)
246
- el.style.display = 'none';
247
- el = document.getElementById('kf-timeline');
248
- if(el)
249
- el.style.display = 'none';
250
- el = document.getElementById('clips-timeline');
251
- if(el)
252
- el.style.display = 'none';
253
-
254
- var timeline = document.getElementById('curves-timeline');
255
- if(timeline) {
256
- timeline.style.display = 'block';
257
- curvesTimeline.resize();
258
- }
259
- else {
260
- curvesTimeline = new LX.CurvesTimeline("curves-timeline", {width: m.root.clientWidth, height: m.parent.root.parentElement.clientHeight - m.root.clientHeight, range: [-1,1]});
261
- curvesTimeline.setSelectedItems(["Item 1", "Item 2", "Item 3"]);
262
- curvesTimeline.setAnimationClip({tracks: [{name: "Item 1.position", values: [0,1,0,-1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 1.scale", values: [0,1,0, 0.5], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 2", values: [0,1,0,1], times: [0.1, 0.2, 0.3, 0.8]}, {name: "Item 3.position", values: [0,0,0,1], times: [0, 0.1, 0.2, 0.3]}, {name: "Item 3.scale", values: [0,1,0], times: [0, 0.1, 0.2, 0.3]}], duration: 1});
263
- bottom.attach(curvesTimeline.root);
264
- curvesTimeline.draw(0);
265
- }
266
- });
267
-
268
- bottom.onresize = bounding => {
269
- if(clipsTimeline)
270
- clipsTimeline.resize( );
271
-
272
- if(kfTimeline)
273
- kfTimeline.resize();
10
+ // Menubar
11
+ {
12
+ area.addMenubar( m => {
274
13
 
275
- if(curvesTimeline)
276
- curvesTimeline.resize();
277
- }
278
- } );
279
-
280
- var bottomPanel = bottom.addPanel({id: "bottom-panel"});
281
- fillBottomPanel( bottomPanel );
282
-
283
- // split right area
284
- var [rup, rbottom] = right.split({type: 'vertical', sizes:["70%","30%"]});
285
-
286
- // Get new content area to fill it
287
- const topTabs = up.addTabs();
288
-
289
- // add canvas to left upper part
290
- var canvas = document.createElement('canvas');
291
- canvas.style.width = "100%";
292
- canvas.style.height = "100%";
293
-
294
- const resizeCanvas = ( bounding ) => {
295
- canvas.width = bounding.width;
296
- canvas.height = bounding.height;
297
- };
298
-
299
- topTabs.add( "Canvas", canvas, { selected: true, onCreate: resizeCanvas } );
300
- topTabs.add( "Debug", document.createElement('div'));
301
-
302
- // add on resize event to control canvas size
303
- topTabs.area.onresize = resizeCanvas;
304
-
305
- topTabs.area.addOverlayButtons( [
306
- [
307
- {
308
- name: "Select",
309
- icon: "fa fa-arrow-pointer",
310
- callback: (value, event) => console.log(value),
311
- selectable: true
312
- },
313
- {
314
- name: "Move",
315
- icon: "fa-solid fa-arrows-up-down-left-right",
316
- callback: (value, event) => console.log(value),
317
- selectable: true
318
- },
319
- {
320
- name: "Rotate",
321
- icon: "fa-solid fa-rotate-right",
322
- callback: (value, event) => console.log(value),
323
- selectable: true
324
- }
325
- ],
326
- {
327
- name: "Lit",
328
- options: ["Lit", "Unlit", "Wireframe"],
329
- callback: (value, event) => console.log(value)
330
- },
331
- [
332
- {
333
- name: "Enable Snap",
334
- icon: "fa fa-table-cells",
335
- callback: (value, event) => console.log(value),
336
- selectable: true
337
- },
338
- {
339
- name: 10,
340
- options: [10, 100, 1000],
341
- callback: value => console.log(value)
342
- }
343
- ], {
344
- name: "Button 4",
345
- // img: "https://webglstudio.org/latest/imgs/mini-icon-gizmo.png",
346
- icon: "fa fa-cube",
347
- callback: (value, event) => console.log(value)
348
- }
349
- ], { float: "htc" } );
350
-
351
- // add panels
352
- var sidePanel = rup.addPanel();
353
- fillPanel( sidePanel );
354
-
355
- const bottomTabs = rbottom.addTabs({ fit: true });
356
- var sideBottomPanel = new LX.Panel();
357
- var sideBottomPanelH = new LX.Panel();
358
- fillRightBottomPanel( sideBottomPanel, 'Vertical' );
359
- fillRightBottomPanel( sideBottomPanelH, 'Horizontal' );
360
-
361
- bottomTabs.add( "Panel V", sideBottomPanel );
362
- bottomTabs.add( "Panel H", sideBottomPanelH );
363
-
364
- const footer = new LX.Footer( {
365
- parent: bottomPanel.root,
366
- columns: [
367
- {
368
- title: "LexGUI",
369
- items: [
370
- { title: "Download", link: "" },
371
- { title: "Documentation", link: "" },
372
- { title: "Web demo", link: "" },
373
- { title: "Source code", link: "" }
374
- ]
375
- },
376
- {
377
- title: "Projects",
378
- items: [
379
- { title: "Animics", link: "" },
380
- { title: "Performs", link: "" }
381
- ]
382
- },
383
- {
384
- title: "Other stuff",
385
- items: [
386
- { title: "Some section", link: "" },
387
- { title: "Just filling", link: "" },
388
- { title: "No more ideas", link: "" },
389
- ]
390
- }
391
- ],
392
- credits: `2019-${ new Date().getUTCFullYear() } Alex Rodríguez and contributors. Website source code on GitHub.`,
393
- socials: [
394
- { title: "Github", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6.0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6.0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3.0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1.0-6.2-.3-40.4-.3-61.4.0.0-70 15-84.7-29.8.0.0-11.4-29.1-27.8-36.6.0.0-22.9-15.7 1.6-15.4.0.0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5.0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9.0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4.0 33.7-.3 75.4-.3 83.6.0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6.0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9.0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>` },
395
- { title: "BlueSky", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M407.8 294.7c-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3zM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3 61.6-9.4 37.5-1.7 21.6 5.5 3.3 13.8.0 41.9.0 58.4S9.1 194 15 213.9c19.5 65.7 89.1 87.9 153.2 80.7 3.3-.5 6.6-.9 10-1.4-3.3.5-6.6 1-10 1.4-93.9 14-177.3 48.2-67.9 169.9C220.6 589.1 265.1 437.8 288 361.1c22.9 76.7 49.2 222.5 185.6 103.4 102.4-103.4 28.1-156-65.8-169.9-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3 64.1 7.1 133.6-15.1 153.2-80.7C566.9 194 576 75 576 58.4s-3.3-44.7-21.6-52.9c-15.8-7.1-40-14.9-103.2 29.8C385.1 81.9 314.1 176.4 288 227.1z"></path></svg>` },
396
- { title: "Mastodon", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5.0.0.0-63.7 28.5-63.7 125.7.0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5.0 01-.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6V190.1c0-49.7-64-51.6-64 6.9v62.5H201V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"></path></svg>` },
397
- { title: "Discord", link: "", icon: `<a class="fa-brands fa-discord"></a>` },
398
- { title: "Reddit", link: "", icon: `<a class="fa-brands fa-reddit"></a>` }
399
- ]
400
- } );
401
-
402
- function loop(dt) {
14
+ m.setButtonImage("lexgui.js", "images/icon.png", () => {window.open("https://jxarco.github.io/lexgui.js/")}, {float: "left"})
403
15
 
404
- var ctx = canvas.getContext("2d");
405
-
406
- // Get values from panel widgets (e.g. color value)
407
- ctx.fillStyle = sidePanel.getValue('Background');
408
-
409
- ctx.fillRect(0, 0, canvas.width, canvas.height);
410
- ctx.font = sidePanel.getValue('Font Size') + "px Monospace";
411
-
412
- ctx.fillStyle = sidePanel.getValue('Font Color');
413
-
414
- const text = sidePanel.getValue('Text');
415
- const pos2D = sidePanel.getValue('2D Position');
416
- ctx.fillText(text, pos2D[0], pos2D[1]);
417
-
418
- if(kfTimeline)
419
- kfTimeline.draw();
420
-
421
- if(clipsTimeline)
422
- clipsTimeline.draw();
423
-
424
- if(curvesTimeline)
425
- curvesTimeline.draw();
426
-
427
- requestAnimationFrame(loop);
428
- }
429
-
430
- // createAssetDialog();
431
-
432
- requestAnimationFrame(loop);
433
-
434
- // **** **** **** **** **** **** **** **** **** **** **** ****
435
-
436
- function fillPanel( panel ) {
16
+ m.add( "Docs", { icon: "fa-solid fa-magnifying-glass", short: "F1", callback: () => { window.open("./docs/") }});
437
17
 
438
- // Add data tree
439
-
440
- let sceneData = {
441
- 'id': 'root',
442
- 'children': [
18
+ const panel = new LX.Panel();
19
+ panel.addButton(null, "Search command...", () => { LX.setCommandbarState( true ) }, { width: "256px", className: "right", buttonClass: "outline left" });
20
+ m.root.appendChild( panel.root.childNodes[ 0 ] );
21
+
22
+ m.addButtons( [
443
23
  {
444
- 'id': 'node_1',
445
- 'children': [
446
- {
447
- 'id': 'node_1_1',
448
- 'icon': 'fa-solid fa-cube',
449
- 'children': [],
450
- 'actions': [
451
- {
452
- 'name': 'Open script',
453
- 'icon': 'fa-solid fa-scroll',
454
- 'callback': function(node) {
455
- console.log(node.id + ": Script opened!")
456
- }
457
- }
458
- ]
459
- }
460
- ]
24
+ title: "Github",
25
+ icon: "fa-brands fa-github",
26
+ callback: (event) => {
27
+ window.open( "https://github.com/jxarco/lexgui.js/", "_blank" );
28
+ }
461
29
  },
462
30
  {
463
- 'id': 'node_2',
464
- 'icon': 'fa-solid fa-circle-play',
465
- 'children': []
31
+ title: "Change Theme",
32
+ icon: "fa-solid fa-sun",
33
+ swap: "fa-solid fa-moon",
34
+ callback: (event, swapValue) => { LX.setTheme( swapValue ? "light" : "dark" ) }
466
35
  }
467
- ]
468
- };
469
-
470
- // This is optional!
471
- const treeIcons = [
472
- {
473
- 'name':'Add node',
474
- 'icon': 'fa-solid fa-plus',
475
- 'callback': () => { console.log("Node added!") }
476
- },
477
- {
478
- 'name':'Instantiate scene',
479
- 'icon': 'fa-solid fa-link',
480
- 'callback': () => { console.log("Scene instantiated!") }
481
- }
482
- ];
483
-
484
- window.tree = panel.addTree("Scene Tree", sceneData, {
485
- icons: treeIcons,
486
- // filter: false,
487
- addDefault: true,
488
- onevent: (event) => {
489
- console.log(event.string());
490
-
491
- switch(event.type) {
492
- case LX.TreeEvent.NODE_SELECTED:
493
- if(event.multiple)
494
- console.log("Selected: ", event.node);
495
- else
496
- console.log(event.node.id + " selected");
497
- break;
498
- case LX.TreeEvent.NODE_DELETED:
499
- if(event.multiple)
500
- console.log("Deleted: ", event.node);
501
- else
502
- console.log(event.node.id + " deleted");
503
- break;
504
- case LX.TreeEvent.NODE_DBLCLICKED:
505
- console.log(event.node.id + " dbl clicked");
506
- break;
507
- case LX.TreeEvent.NODE_CONTEXTMENU:
508
- const m = event.panel;
509
- m.add( "Components/Transform");
510
- m.add( "Components/MeshRenderer");
511
- break;
512
- case LX.TreeEvent.NODE_DRAGGED:
513
- console.log(event.node.id + " is now child of " + event.value.id);
514
- break;
515
- case LX.TreeEvent.NODE_RENAMED:
516
- console.log(event.node.id + " is now called " + event.value);
517
- break;
518
- case LX.TreeEvent.NODE_VISIBILITY:
519
- console.log(event.node.id + " visibility: " + event.value);
520
- break;
521
- }
522
- }
523
- });
524
-
525
- // add widgets to panel branch
526
- panel.branch("Preferences", {icon: "fa-solid fa-gear"});
527
- panel.addButton(null, "Show Notifications" + LX.badge("+99", "accent sm"));
528
- panel.addCounter("Calories Counter ", 350, (v) => { console.log( v + " calories!" ) }, { label: "CALORIES/DAY", max: 500 });
529
- panel.addButton("Colored Tiny Button", "Click here!", () => {}, { buttonClass: "primary xs" });
530
- panel.addButton("Colored Small Button", "Click here!", () => {}, { buttonClass: "accent sm" });
531
- panel.addButton("A Classic Button", "Click here!", () => {}, { buttonClass: "md" });
532
- panel.addCheckbox("Check me, Please", false, (value, event) => {
533
- console.log(value);
534
- }, { className: "secondary" });
535
- panel.sameLine(2);
536
- panel.addToggle("Colored Toggle", false, (value, event) => {
537
- console.log(value);
538
- }, { className: "accent", nameWidth: "50%" });
539
- panel.addToggle("Outlined Checkbox ", false, (value, event) => {
540
- console.log(value);
541
- }, { className: "secondary outline", nameWidth: "50%" });
542
- panel.addFile("I'm a File Input", data => { console.log(data) }, { disabled: true } );
543
- panel.addDropdown("Best Engine", ["Godot", "Unity", "Unreal Engine"], "Unity", (value, event) => {
544
- console.log(value);
545
- });
546
- panel.addDropdown("Best Logo", [{value:"Godot", src: "https://godotengine.org/assets/press/logo_vertical_color_light.png"}, {value: "Unity", src: "https://logos-world.net/wp-content/uploads/2023/01/Unity-Logo.png"}, {value:"Unreal Engine", src: "https://cdn2.unrealengine.com/ue-logo-stacked-unreal-engine-w-677x545-fac11de0943f.png"}], "Godot", (value, event) => {
547
- console.log(value);
548
- }, {filter: true});
549
- panel.addDropdown("Best Gif", [{value:"Godot", src: "https://i.redd.it/4vepr95bye861.gif"}, {value: "Unity", src: "https://i.gifer.com/origin/db/db3cb258e9bbb78c5851a000742e5468_w200.gif"}, {value:"Unreal Engine", src: "https://d3kjluh73b9h9o.cloudfront.net/original/4X/e/0/d/e0deb23c10cc7852c6ab91c28083e27f9c8228f8.gif"}], "Godot", (value, event) => {
550
- console.log(value);
551
- }, {filter: true});
552
-
553
- panel.addVector3("Im a Vec3", [0.1, 0.4, 0.5], (value, event) => {
554
- console.log(value);
555
- });
556
- panel.addLayers("Layers", 10, (value, event) => {
557
- console.log(value);
558
- });
559
- panel.addArray("Array", ['GPTeam', 'Blat Panthers', 'Blat Bunny'], (value, event) => {
560
- console.log(value);
561
- });
562
- panel.addTags("Game Tags", "2d, karate, ai, engine, ps5, console", (value, event) => {
563
- console.log(value);
564
- });
565
- panel.addComboButtons("Alignment", [
566
- {
567
- value: 'left',
568
- icon: 'fa fa-align-left',
569
- callback: (value, event) => {
570
- console.log(value);
571
- }
572
- }, {
573
- value: 'center',
574
- icon: 'fa fa-align-center',
575
- callback: (value, event) => {
576
- console.log(value);
577
- }
578
- }, {
579
- value: 'right',
580
- icon: 'fa fa-align-right',
581
- callback: (value, event) => {
582
- console.log(value);
583
- }
584
- }
585
- ], {selected: "center"});
586
- panel.addList(null, ['GPTeam', 'Blat Bunny', ['Blat Panthers', 'fa-solid fa-paw']], 'Blat Panthers', (value, event) => {
587
- console.log(value);
588
- });
589
- const opacityValues = [
590
- [0.2, 0.3146875],
591
- [0.417313915857606, 0.8946875000000003],
592
- [0.5495145631067961, 0.6746875],
593
- [1, 1]
594
- ];
595
- panel.addCurve("Opacity", opacityValues, (value, event) => {
596
- console.log(value);
597
- });
598
- panel.addPad("2D Pad", [0.5, 0.5], (value, event) => {
599
- console.log(value);
600
- }, { padSize: "100px", min: -1, max: 2 });
601
- panel.addSize("Screen Res", [1280, 720], (value, event) => {
602
- console.log(value);
603
- }, { units: "p", precision: 0 });
604
-
605
- // another branch
606
- panel.branch("Canvas", {icon: "fa-solid fa-palette", filter: true});
607
- panel.addColor("Background", "#b7a9b1");
608
- panel.addText("Text", "Lexgui.js @jxarco", null, {placeholder: "e.g. ColorPicker", icon: "fa fa-font"});
609
- panel.addColor("Font Color", [1, 0.1, 0.6], (value, event) => {
610
- console.log("Font Color: ", value);
36
+ ], { float: "right" });
611
37
  });
612
- panel.addNumber("Font Size", 36, (value, event) => {
613
- console.log(value);
614
- }, { min: 1, max: 48, step: 1, units: "px"});
615
- panel.addVector2("2D Position", [250, 350], (value, event) => {
616
- console.log(value);
617
- }, { min: 0, max: 1024 });
618
- panel.addSeparator();
619
- panel.addTitle("Configuration (Im a title)");
620
- panel.addCheckbox("Toggle me", true, (value, event) => {
621
- console.log(value);
622
- }, { suboptions: (p) => {
623
- p.addText(null, "Suboption 1");
624
- p.addNumber("Suboption 2", 12);
625
- } });
626
- panel.addFile("Image", data => { console.log(data) }, {} );
627
- panel.merge();
628
-
629
- // This is outside a branch
630
- panel.addText("Im out :(", "", null, { placeholder: "Alone..." });
631
- panel.addVector4("Im a Vec4", [0.3, 0.3, 0.5, 1], (value, event) => {
632
- console.log(value);
633
- });
634
- panel.addButton(null, "Click me, Im Full Width...");
635
- panel.addButton("Test Button", "Reduced width...");
636
- panel.addBlank(12);
637
38
  }
638
39
 
639
- function fillRightBottomPanel( panel, tab ) {
40
+ // Header
41
+ {
42
+ const header = LX.makeContainer( [ null, "auto" ], "col", {
43
+ border: "1px solid rgba(255, 255, 255, 0.08)",
44
+ padding: "36px 24px 36px 24px",
45
+ gap: "8px"
46
+ } );
640
47
 
641
- panel.clear();
642
-
643
- panel.branch("Bottom", {icon: "fa-solid fa-table-list"});
644
-
645
- if(tab == 'Horizontal')
646
- {
647
- panel.addTabs([
648
- {
649
- name: "First tab",
650
- icon: "fa-brands fa-discord",
651
- onCreate: p => {
652
- p.addTitle("Discord tab");
653
- p.addButton(null, "Connect");
654
- },
655
- onSelect: p => {
656
- console.log( p );
657
- }
658
- },
659
- {
660
- name: "Second tab",
661
- icon: "fa-brands fa-twitter",
662
- onCreate: p => {
663
- p.addTitle("Twitter tab");
664
- p.addText("Tweet", "", null, {placeholder: "Tyler Rake 2"});
665
- }
666
- },
667
- {
668
- name: "Third tab",
669
- icon: "fa-brands fa-github",
670
- onCreate: p => {
671
- p.addTitle("Github tab");
672
- p.addButton(null, "Go", () => {window.open("https://github.com/jxarco/lexgui.js/")});
673
- }
674
- }
675
- ], { vertical: false /*, showNames: true */});
676
-
677
- panel.addText(null, "Widgets below are out the tabs", null, { disabled: true })
678
-
679
- // update panel values uising widget name
680
- panel.addNumber("HeadRoll Value", 0, (value, event) => {
681
- panel.setValue('HeadRoll', value);
682
- }, { min: -1, max: 1, step: 0.1 });
683
- panel.addProgress("HeadRoll", 0, { min: -1, max: 1, low: -0.25, high: 0.25, optimum: 0.75, showValue: true, editable: true, callback: (value, event) => {
684
- panel.setValue('HeadRoll Value', value);
685
- } });
686
- }
687
- else if(tab == 'Vertical')
688
- {
689
- panel.addTabs([
690
- {
691
- name: "First tab",
692
- icon: "fa-brands fa-discord",
693
- onCreate: (p, content) => {
694
- p.addTitle("Discord tab");
695
- p.addButton("Apply", "Add button to branch", (value, event) => {
696
- p.queue( content );
697
- p.addButton(null, "Hello");
698
- p.clearQueue();
699
- });
700
- }
701
- },
702
- {
703
- name: "Second tab",
704
- icon: "fa-brands fa-twitter",
705
- onCreate: p => {
706
- p.addTitle("Twitter tab");
707
- p.addText("Tweet", "", null, {placeholder: "Tyler Rake 2"});
708
- }
709
- },
710
- {
711
- name: "Third tab",
712
- icon: "fa-brands fa-github",
713
- onCreate: p => {
714
- p.addTitle("Github tab");
715
- p.addButton(null, "Go", (value, event) => {window.open("https://github.com/jxarco/lexgui.js/")});
716
- }
717
- }
718
- ]);
719
-
720
- /************** */
721
- // Custom Widget
722
-
723
- LX.ADD_CUSTOM_WIDGET( "Shader", {
724
- icon: "fa-cube",
725
- default: {
726
- position: [0, 0],
727
- velocity: [0, 0, 0],
728
- color: [0, 0, 0, 0],
729
- hexColor: "#000",
730
- highRes: false
731
- }
732
- });
733
-
734
- const shaderInstance = {
735
- 'hexColor': "#f5f505",
736
- 'highRes': true
737
- };
738
-
739
- panel.addShader( "A Shader", shaderInstance, (instance) => { console.log(instance) } );
740
- panel.addShader( "Empty Instance", null );
741
-
742
- /************** */
743
- }
744
-
745
- panel.merge();
746
- }
747
-
748
- function fillBottomPanel( panel ) {
48
+ header.innerHTML = `
49
+ <a>Get started with LexGUI.js</a>
50
+ <h1>Build your application interface</h1>
51
+ <p style="max-width:42rem">A set of beautifully-designed, accessible components and a code distribution platform.
52
+ Works with your favorite frameworks. Open Source. Open Code.</p>
53
+ `;
749
54
 
750
- // add widgets to panel branch
751
- panel.branch("Information", {icon: "fa fa-circle-info"});
752
- panel.addText("Camera", "Canon EOS 80D", null, {disabled: true});
753
- panel.addText("Text", "Warning text", null, { warning: true });
754
- const patternOptions = { uppercase: true }
755
- panel.addText("Text With Validator Pattern", "", (value, event) => {
756
- console.log(value);
757
- }, { pattern: LX.buildTextPattern( patternOptions ) });
758
- panel.addTextArea("Notes", "", (value, event) => {
759
- console.log(value);
760
- }, { placeholder: 'Some notes...' });
761
- panel.addKnob("A Small but disabled Knob", 4, 0, 200, value => { console.log( value ) }, { size: 'sm', disabled: true });
762
- panel.addKnob("A Knob", 4, 0, 200, value => { console.log( value ) } );
763
- panel.addKnob("A Big Knob with Snap", 4, 0, 200, value => { console.log( value ) }, { size: 'bg', snap: 4 });
764
- panel.addButton("Apply", "Add button to branch", (value, event) => {
765
- const branch = panel.getBranch("Information");
766
- panel.queue( branch.content );
767
- panel.addButton(null, "Hello");
768
- panel.clearQueue();
769
- });
770
-
771
- panel.branch("A collapsed branch", { closed: true });
772
- panel.addText(null, "Nothing here", null, { disabled: true });
773
- panel.merge();
55
+ area.attach( header );
774
56
  }
775
57
 
776
- function createLoginForm() {
58
+ // Content
59
+ {
60
+ area.attach( document.createElement('br') );
777
61
 
778
- let dialog = new LX.Dialog('Login', panel => {
62
+ const tabs = area.addTabs( { sizes: [ "auto", "auto" ] } );
779
63
 
780
- const formData = {
781
- Username: {
782
- value: "",
783
- placeholder: "Enter username",
784
- icon: "fa fa-user",
785
- pattern: LX.buildTextPattern( { minLength: 3 } )
786
- },
787
- Password: {
788
- value: "",
789
- type: "password",
790
- placeholder: "Enter password",
791
- icon: "fa fa-key",
792
- pattern: LX.buildTextPattern( { lowercase: true, uppercase: true, digit: true, minLength: 6 } )
793
- }
794
- };
64
+ const examplesContainer = LX.makeContainer( [ "auto", "850px" ], "", {
65
+ backgroundColor: "red"
66
+ } );
795
67
 
796
- panel.addForm("Test form", formData, (value, event) => {
797
- console.log(value);
798
- }, { actionName: "Login" });
68
+ const tasksContainer = LX.makeContainer( [ "auto", "850px" ], "", {
69
+ backgroundColor: "red"
70
+ } );
799
71
 
800
- panel.addLabel( "Or", { float: 'center' } );
72
+ const codeContainer = LX.makeContainer( [ "auto", "850px" ], "", {
73
+ backgroundColor: "red"
74
+ } );
801
75
 
802
- panel.addButton( null, "Sign up", ( value, event ) => { });
76
+ const audioContainer = LX.makeContainer( [ "auto", "850px" ], "", {
77
+ backgroundColor: "red"
78
+ } );
803
79
 
804
- }, { close: true, minimize: false, size: ["25%"], scroll: true, resizable: true, draggable: true });
80
+ tabs.add( "Examples", examplesContainer, { selected: true } );
81
+ tabs.add( "Tasks", tasksContainer );
82
+ tabs.add( "Code", codeContainer );
83
+ tabs.add( "Audio", audioContainer );
805
84
  }
806
85
 
807
- function createAssetDialog() {
808
-
809
- let dialog = new LX.Dialog('Non Manual Features lexemes', (p) => {
810
-
811
- const previewActions = [
86
+ // Footer
87
+ {
88
+ const footer = new LX.Footer( {
89
+ parent: area.root,
90
+ columns: [
812
91
  {
813
- name: 'Print Clip',
814
- type: 'clip',
815
- callback: ( item ) => {
816
- console.log(item);
817
- }
92
+ title: "LexGUI",
93
+ items: [
94
+ { title: "Download", link: "" },
95
+ { title: "Documentation", link: "" },
96
+ { title: "Web demo", link: "" },
97
+ { title: "Source code", link: "" }
98
+ ]
818
99
  },
819
100
  {
820
- name: 'Print Image',
821
- type: 'image',
822
- callback: ( item ) => {
823
- console.log(item);
824
- }
101
+ title: "Projects",
102
+ items: [
103
+ { title: "Animics", link: "" },
104
+ { title: "Performs", link: "" }
105
+ ]
825
106
  },
826
107
  {
827
- name: 'Common',
828
- callback: ( item ) => {
829
- console.log(item);
830
- }
831
- }
832
- ];
833
-
834
- var assetView = new LX.AssetView({
835
- skip_browser: true,
836
- skip_navigation: true,
837
- preview_actions: previewActions
838
- });
839
-
840
- p.attach( assetView );
841
- let assetData = [];
842
- const values = ['brow_lowerer.png', 'godot_pixelart.png', 'godot_canvas.png' ];
843
-
844
- for(let i = 0; i < values.length; i++){
845
- let data = {
846
- id: values[i],
847
- type: i == 0 ? "clip" : "image",
848
- src: "data/" + values[i].toLowerCase(),
849
- }
850
- assetData.push(data);
851
- }
852
-
853
- assetView.load( assetData, (e,v) => {
854
- switch(e.type) {
855
- case LX.AssetViewEvent.ASSET_SELECTED:
856
- if(e.multiple)
857
- console.log("Selected: ", e.item);
858
- else
859
- console.log(e.item.id + " selected");
860
- break;
861
- case LX.AssetViewEvent.ASSET_DELETED:
862
- console.log(e.item.id + " deleted");
863
- break;
864
- case LX.AssetViewEvent.ASSET_CLONED:
865
- console.log(e.item.id + " cloned");
866
- break;
867
- case LX.AssetViewEvent.ASSET_RENAMED:
868
- console.log(e.item.id + " is now called " + e.value);
869
- break;
108
+ title: "Other stuff",
109
+ items: [
110
+ { title: "Some section", link: "" },
111
+ { title: "Just filling", link: "" },
112
+ { title: "No more ideas", link: "" },
113
+ ]
870
114
  }
871
- })
872
- },{ title:'Lexemes', close: true, minimize: false, size: ["80%"], scroll: true, resizable: true, draggable: true });
873
- }
874
-
875
- LX.popup("Hello! I'm a popup :)", null, {position: ["50px", "100px"]});
115
+ ],
116
+ credits: `2019-${ new Date().getUTCFullYear() } Alex Rodríguez and contributors. Website source code on GitHub.`,
117
+ socials: [
118
+ { title: "Github", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 496 512"><path d="M165.9 397.4c0 2-2.3 3.6-5.2 3.6-3.3.3-5.6-1.3-5.6-3.6.0-2 2.3-3.6 5.2-3.6 3-.3 5.6 1.3 5.6 3.6zm-31.1-4.5c-.7 2 1.3 4.3 4.3 4.9 2.6 1 5.6.0 6.2-2s-1.3-4.3-4.3-5.2c-2.6-.7-5.5.3-6.2 2.3zm44.2-1.7c-2.9.7-4.9 2.6-4.6 4.9.3 2 2.9 3.3 5.9 2.6 2.9-.7 4.9-2.6 4.6-4.6-.3-1.9-3-3.2-5.9-2.9zM244.8 8C106.1 8 0 113.3.0 252c0 110.9 69.8 205.8 169.5 239.2 12.8 2.3 17.3-5.6 17.3-12.1.0-6.2-.3-40.4-.3-61.4.0.0-70 15-84.7-29.8.0.0-11.4-29.1-27.8-36.6.0.0-22.9-15.7 1.6-15.4.0.0 24.9 2 38.6 25.8 21.9 38.6 58.6 27.5 72.9 20.9 2.3-16 8.8-27.1 16-33.7-55.9-6.2-112.3-14.3-112.3-110.5.0-27.5 7.6-41.3 23.6-58.9-2.6-6.5-11.1-33.3 2.6-67.9 20.9-6.5 69 27 69 27 20-5.6 41.5-8.5 62.8-8.5s42.8 2.9 62.8 8.5c0 0 48.1-33.6 69-27 13.7 34.7 5.2 61.4 2.6 67.9 16 17.7 25.8 31.5 25.8 58.9.0 96.5-58.9 104.2-114.8 110.5 9.2 7.9 17 22.9 17 46.4.0 33.7-.3 75.4-.3 83.6.0 6.5 4.6 14.4 17.3 12.1C428.2 457.8 496 362.9 496 252 496 113.3 383.5 8 244.8 8zM97.2 352.9c-1.3 1-1 3.3.7 5.2 1.6 1.6 3.9 2.3 5.2 1 1.3-1 1-3.3-.7-5.2-1.6-1.6-3.9-2.3-5.2-1zm-10.8-8.1c-.7 1.3.3 2.9 2.3 3.9 1.6 1 3.6.7 4.3-.7.7-1.3-.3-2.9-2.3-3.9-2-.6-3.6-.3-4.3.7zm32.4 35.6c-1.6 1.3-1 4.3 1.3 6.2 2.3 2.3 5.2 2.6 6.5 1 1.3-1.3.7-4.3-1.3-6.2-2.2-2.3-5.2-2.6-6.5-1zm-11.4-14.7c-1.6 1-1.6 3.6.0 5.9 1.6 2.3 4.3 3.3 5.6 2.3 1.6-1.3 1.6-3.9.0-6.2-1.4-2.3-4-3.3-5.6-2z"></path></svg>` },
119
+ { title: "BlueSky", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path d="M407.8 294.7c-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3zM288 227.1C261.9 176.4 190.9 81.9 124.9 35.3 61.6-9.4 37.5-1.7 21.6 5.5 3.3 13.8.0 41.9.0 58.4S9.1 194 15 213.9c19.5 65.7 89.1 87.9 153.2 80.7 3.3-.5 6.6-.9 10-1.4-3.3.5-6.6 1-10 1.4-93.9 14-177.3 48.2-67.9 169.9C220.6 589.1 265.1 437.8 288 361.1c22.9 76.7 49.2 222.5 185.6 103.4 102.4-103.4 28.1-156-65.8-169.9-3.3-.4-6.7-.8-10-1.3 3.4.4 6.7.9 10 1.3 64.1 7.1 133.6-15.1 153.2-80.7C566.9 194 576 75 576 58.4s-3.3-44.7-21.6-52.9c-15.8-7.1-40-14.9-103.2 29.8C385.1 81.9 314.1 176.4 288 227.1z"></path></svg>` },
120
+ { title: "Mastodon", link: "", icon: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path d="M433 179.1c0-97.2-63.7-125.7-63.7-125.7-62.5-28.7-228.6-28.4-290.5.0.0.0-63.7 28.5-63.7 125.7.0 115.7-6.6 259.4 105.6 289.1 40.5 10.7 75.3 13 103.3 11.4 50.8-2.8 79.3-18.1 79.3-18.1l-1.7-36.9s-36.3 11.4-77.1 10.1c-40.4-1.4-83-4.4-89.6-54a102.5 102.5.0 01-.9-13.9c85.6 20.9 158.7 9.1 178.8 6.7 56.1-6.7 105-41.3 111.2-72.9 9.8-49.8 9-121.5 9-121.5zm-75.1 125.2h-46.6V190.1c0-49.7-64-51.6-64 6.9v62.5H201V197c0-58.5-64-56.6-64-6.9v114.2H90.2c0-122.1-5.2-147.9 18.4-175 25.9-28.9 79.8-30.8 103.8 6.1l11.6 19.5 11.6-19.5c24.1-37.1 78.1-34.8 103.8-6.1 23.7 27.3 18.4 53 18.4 175z"></path></svg>` },
121
+ { title: "Discord", link: "", icon: `<a class="fa-brands fa-discord"></a>` },
122
+ { title: "Reddit", link: "", icon: `<a class="fa-brands fa-reddit"></a>` }
123
+ ]
124
+ } );
125
+ }