squared 0.4.17 → 0.4.19

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.
data/README.md CHANGED
@@ -1,1367 +1,729 @@
1
- # squared 5.6
1
+ # squared 0.4
2
2
 
3
- ## Documentation
3
+ * [source](https://github.com/anpham6/squared-ruby)
4
+ * [docs](https://squared.readthedocs.io)
4
5
 
5
- ### HTML
6
+ ## Version Compatibility
6
7
 
7
- * [squared](https://squared.readthedocs.io)
8
- * [E-mc](https://e-mc.readthedocs.io)
8
+ | Date | squared | Min | Max | Git |
9
+ | :--------: | ------: | -----: | -----: | -----: |
10
+ | 2024-12-07 | 0.1.0 | 2.4.0 | 3.3.6 | 2.39 |
11
+ | 2025-01-07 | 0.2.0 | 2.4.0 | 3.4.0 | 2.39 |
12
+ | 2025-02-07 | 0.3.0 | 2.4.0 | 3.4.1 | 2.39 |
13
+ | 2025-03-06 | 0.4.0 | 2.4.0 | 3.4.2 | 2.39 |
9
14
 
10
- > [!NOTE]
11
- > Content in the README was migrated into `Read the Docs`. The file is no longer fully maintained.
12
-
13
- ### README
14
-
15
- * [squared-express](https://github.com/anpham6/squared-express#readme)
16
- * [E-mc](https://github.com/anpham6/e-mc#readme)
17
- * [Pi-r](https://github.com/anpham6/pi-r#readme)
18
- * [Pi-r2](https://github.com/anpham6/pi-r2#readme)
15
+ The range chart indicates the latest Ruby tested against at the time of release.
19
16
 
20
17
  ## Installation
21
18
 
22
- * NodeJS 18
23
-
24
- ### NPX
25
-
26
19
  ```sh
27
- > npm init
28
- > npm i sqd-cli sqd-serve
29
-
30
- > npx sqd init
31
- # OR
32
- > npx sqd init --public --local-serve # Same as squared-express
33
-
34
- > npx sqd serve --access-all
35
- # OR
36
- > node serve.cjs --access-all
20
+ gem install squared
37
21
  ```
38
22
 
39
- ```sh
40
- > npm init
41
- > npm i squared sqd-serve
42
-
43
- > mkdir dist html
44
- > cp -r ./node_modules/squared/dist/* ./dist
45
- > cp ./node_modules/squared/html/* ./html # optional
46
- > cp ./node_modules/sqd-serve/config/json/* . # yaml
23
+ ### Optional
47
24
 
48
- > npx serve
49
- ```
50
-
51
- ### GitHub
25
+ * [Repo](https://source.android.com/docs/setup/reference/repo)
26
+ * https://github.com/anpham6/squared-repo
27
+ * Python 3.6
28
+ * Not compatible with Windows
52
29
 
53
30
  ```sh
54
- > git clone https://github.com/anpham6/squared
55
- > cd squared
56
-
57
- > npm i
58
- > npm run prod
59
-
60
- > cd ..
61
-
62
- > git clone https://github.com/anpham6/squared-express
63
- > cd squared-express
64
-
65
- > npm i
66
- > npm run prod
67
- > npm run deploy # deploy:yaml
68
-
69
- > cd ../squared
70
-
71
- > node serve.cjs --access-all # squared.{json,yml}
31
+ mkdir -p ~/.bin
32
+ PATH="${HOME}/.bin:${PATH}"
33
+ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
34
+ chmod a+rx ~/.bin/repo
72
35
  ```
73
36
 
74
- ### Repo
75
-
76
- * Python 3.6+
77
- * [Commands](https://source.android.com/docs/setup/reference/repo)
37
+ ## Example - Rakefile
78
38
 
79
- #### Install
39
+ Projects from any accessible folder can be added relative to the parent directory (e.g. *REPO_ROOT*) or absolutely. The same Rakefile can also manage other similarly cloned `Repo` repositories remotely by setting the `REPO_ROOT` environment variable to the location. Missing projects will simply be excluded from the task runner.
80
40
 
81
- ```sh
82
- mkdir -p ~/bin/repo
83
- PATH="${HOME}/bin:${PATH}"
41
+ ```ruby
42
+ require "squared"
84
43
 
85
- curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
86
- chmod a+rx ~/bin/repo
44
+ require "squared/workspace"
45
+ require "squared/workspace/repo" # Optional
46
+ require "squared/workspace/project/node" #
47
+ require "squared/workspace/project/python" #
48
+ require "squared/workspace/project/ruby" #
49
+ require "squared/workspace/project/docker" #
87
50
  # OR
88
- scripts/repo-install.sh ~/bin
89
- ```
90
-
91
- #### Usage
92
-
93
- ```sh
94
- mkdir workspaces
95
- cd workspaces
96
-
97
- repo init -u https://github.com/anpham6/squared-repo -m latest.xml
98
- repo sync -j4
51
+ require "squared/app" # All workspace related modules
99
52
 
100
- cd squared
101
- npm i
102
- ```
53
+ # NODE_ENV = production
103
54
 
104
- #### Ruby
105
-
106
- Workspace management uses [Ruby](https://www.ruby-lang.org/en/documentation/installation) for syncing and building. It is not installed by default on most operating systems.
107
-
108
- ```sh
109
- mkdir workspaces
110
- cd workspaces # REPO_ROOT
111
-
112
- wget https://unpkg.com/squared/Rakefile
113
-
114
- rake -T # List tasks
115
-
116
- # REPO_BUILD={dev,prod}
117
- # PIPE_FAIL=1
118
- rake repo:init # nightly
119
- rake repo:init[latest] # squared
120
- rake repo:init[0.11.x] # e-mc
121
- # OR
122
- REPO_ROOT=/tmp/123 NODE_INSTALL=pnpm repo:init
123
- # OR - without Repo
124
- rake clone:node # master
125
- rake clone # node + docs
126
- ```
127
-
128
- > [!TIP]
129
- > Use the supplied Rakefile inside the **squared** project folder once the source has been downloaded.
130
-
131
- #### Docker
132
-
133
- ```sh
134
- # NODE_TAG=latest
135
- # RUBY_VERSION=2.4.0-3.4.0
136
- # MANIFEST=nightly,prod,latest,android
137
- # BUILD={dev,prod}
138
- # DEV={0,1,local}
139
- # DOCS=any
140
- # PIPE_FAIL={0,1}
141
- # PORT=3000
142
- docker build -t squared --build-arg MANIFEST=prod --build-arg NODE_ENV=production .
143
- docker build -t node --build-arg NODE_TAG=22 --build-arg NODE_INSTALL=pnpm -f Dockerfile.slim .
144
- NODE=22 docker buildx bake node
145
- # OR
146
- docker build -t ruby --build-arg RUBY_TAG=3.4.0 --build-arg NODE_VERSION=22 --build-arg PIPE_FAIL=0 -f Dockerfile.ruby .
147
- RUBY=3.4.0 docker buildx bake ruby
55
+ # REPO_ROOT = /workspaces |
56
+ # REPO_HOME = /workspaces/squared | Dir.pwd
57
+ # rake = /workspaces/squared/Rakefile | main?
148
58
  # OR
149
- docker build -t nginx --build-arg NGINX_VERSION=1.27 --build-arg PORT=3000 --build-arg NODE_VERSION=20 -f Dockerfile.nginx .
150
- NGINX=1.27 docker buildx bake nginx
151
-
152
- # Express
153
- docker run -it --name express --rm -p 3000:3000 \
154
- --mount type=bind,src=${PWD},dst=/workspaces/squared/.config \
155
- --mount type=bind,src=${PWD}/html,dst=/workspaces/squared/www \
156
- squared
157
-
158
- docker run -it --name express --rm -p 443:443 --build-arg PORT=443 squared \
159
- serve --access-all --https --env=production
160
-
161
- # Terminal
162
- docker run -it --name debian squared /bin/bash # irb
163
- ```
164
-
165
- ### Browser
166
-
167
- * Download (squared@version): https://unpkg.com/squared
168
- * Global JS variable: **squared**
169
- * ES2018
170
-
171
- > - https://unpkg.com/squared/dist/squared.min.js
172
- > - https://unpkg.com/squared/dist/squared.base-dom.min.js
173
- > - https://unpkg.com/squared/dist/vdom.framework.min.js
174
-
175
- > * https://unpkg.com/squared/dist/squared.min.js
176
- > * https://unpkg.com/squared/dist/vdom-lite.framework.min.js
177
-
178
- > [!WARNING]
179
- > Safari is completely untested and is not an officially supported browser.
180
-
181
- ## Usage
182
-
183
- Library files are in the `/dist` folder. A minimum of **two** files are required to run *squared*.
184
-
185
- 1. squared
186
- 2. squared-base - *required: except vdom-lite*
187
- 3. **squared-svg** - *optional*
188
- 4. framework (e.g. **android** | **chrome** | vdom | vdom-lite)
189
- 5. extensions - *optional*
190
-
191
- Usable combinations: 1-2-4 + 1-2-4-5 + 1-2-3-4-5 + 1-vdom-lite
192
-
193
- File bundles for common combinations are available in the `/dist/bundles` folder and do not require a call to **setFramework**.
194
-
195
- > [!WARNING]
196
- > Libraries in bold are transpiled with **ES2020**.
197
-
198
- ### Example: android
199
-
200
- The primary function `parseDocument` can be called on multiple elements and multiple times per session. The application will continuously and progressively build the layout files into a single entity with combined shared resources.
201
-
202
- * ES2020
203
-
204
- > [!CAUTION]
205
- > Using `parseDocumentSync` is not recommended when your page has images or fonts.
206
-
207
- ```html
208
- <script src="/dist/squared.min.js"></script>
209
- <script src="/dist/squared.base.min.js"></script>
210
- <script src="/dist/squared.svg.min.js"></script>
211
- <script src="/dist/android.framework.min.js"></script>
212
- <script>
213
- squared.settings.targetAPI = 35; // Optional
214
-
215
- document.addEventListener("DOMContentLoaded", async () => {
216
- squared.setFramework(android, {/* settings */});
217
-
218
- await squared.parseDocument(): Node // document.body (default)
219
- // OR
220
- await squared.parseDocument(/* HTMLElement */, /* "fragment-id" */, /* ...etc */): Node[]
221
- // OR
222
- await squared.parseDocument(
223
- { // Custom settings do not affect other layouts
224
- element: document.body,
225
- projectId: "project-1", // Default is "_"
226
- resourceQualifier: "land", // "res/*" folder suffix
227
- /* OR */
228
- resourceQualifier: {
229
- suffix: "land", // Used for "true" or "undefined" groups (optional)
230
- layout: true, // Will copy to "res/layout-land" when "suffix" is defined
231
- string: undefined, // Will copy to default location "res/values" when "suffix" is undefined
232
- font: false, // Will not copy anything to "res/font" or "res/font-land"
233
- image: "hdpi", // Will copy to "res/drawable-hdpi"
234
- video: "w720dp", // Will copy to "res/raw-w720dp"
235
- audio: "w720dp", // Same as "video" and treated separately
236
- animation: "v34", // Will copy to "res/anim-v34"
237
- menu: "" // Will copy to default location "res/menu"
238
- /* integer + fraction + array + color + dimension + style + theme = Same as "string" */
239
- },
240
- enabledMultiline: false,
241
- enabledSubstitute: true,
242
- include: ["android.substitute"], // Automatically removed after finalize
243
- exclude: ["squared.list", "squared.grid"], // Disabled only during parseDocument
244
- excludeQuery: [{
245
- selector: "main > article", // Hide elements
246
- /* OR */
247
- resource: squared.base.lib.constant.NODE_RESOURCE.BOX_STYLE, // Exclusions during processing
248
- procedure: squared.base.lib.constant.NODE_PROCEDURE.OPTIMIZATION,
249
- section: squared.base.lib.constant.APP_SECTION.DOM_TRAVERSE
250
- }],
251
- customizationsBaseAPI: -1,
252
- observe(mutations, observer, settings) { // Uses MutationObserver
253
- squared.reset(); // Required when calling "parseDocument" after a File action
254
- squared.parseDocument(settings).then(() => {
255
- squared.copyTo("/path/project", { modified: true }).then(response => console.log(response));
256
- });
257
- },
258
- afterCascade(sessionId, node) {/* Restore previous state */},
259
- beforeRender(layout: LayoutUI) {/* Edit initial values */},
260
- afterFinalize(node: NodeUI) {/* Edit controller values */}
261
- },
262
- { // Only "element" is required
263
- element: "fragment-1",
264
- projectId: "project-1", // Implicit once projectId is not "_"
265
- resourceQualifier: "land",
266
- pathname: "app/src/main/res/layout-hdpi", // Will not be overridden by resourceQualifier "land"
267
- filename: "fragment.xml",
268
- baseLayoutAsFragment: {
269
- name: "androidx.navigation.fragment.NavHostFragment",
270
- documentId: "main_content",
271
- app: {
272
- navGraph: "@navigation/product_list_graph",
273
- defaultNavHost: "true"
274
- }
275
- },
276
- beforeCascade(sessionId) {
277
- document.getElementById("fragment-id").style.display = "block"; // Use inline styles
278
- }
279
- }
280
- );
281
- await squared.parseDocument({
282
- element: "fragment-2",
283
- projectId: "sqd2", // Explicit
284
- resourceQualifier: "port", // Will not conflict with projectId "project-1"
285
- enabledFragment: true,
286
- fragmentableElements: [
287
- {
288
- selector: "main", // querySelector
289
- name: "androidx.navigation.fragment.NavHostFragment",
290
- filename: "navigation.xml",
291
- documentId: "main_content"
292
- },
293
- "main > article" // Declarative double nested fragments are invalid (querySelectorAll)
294
- ],
295
- options: {
296
- "android.resource.fragment": {
297
- dynamicNestedFragments: true // FragmentContainerView or FrameLayout as the container (name and tag are ignored)
298
- }
299
- }
300
- });
301
- // OR - Chromium
302
- squared.prefetch("css").then(() => squared.parseDocument()); // Cross-origin support
303
- Promise.all(
304
- squared.prefetch("css", true), // All stylesheets
305
- squared.prefetch("css", "./undetected.css", element.shadowRoot),
306
- squared.prefetch("svg", "http://embedded.example.com/icon.svg", "../images/android.svg")
307
- )
308
- .then(() => squared.parseDocument());
309
-
310
- // Modify attributes
311
-
312
- const body = squared.findDocumentNode(document.body);
313
- body.android("layout_width", "match_parent");
314
- body.lockAttr("android", "layout_width");
315
-
316
- await squared.close(/* projectId */); // Next call to parseDocument will reset project (optional)
317
-
318
- squared.kill("30s").then(result => {/* killed when result > 0 */}); // Abort next request in 30 seconds
319
-
320
- // File actions - implicitly calls "close"
321
-
322
- await squared.save(/* "project-1" */, /* broadcastId | timeout */); // Uses defaults from settings
323
- // OR
324
- await squared.saveAs(/* archive filename */, { projectId: "project-1" });
325
- await squared.saveAs(/* archive filename */, { timeout: 10 }); // Kills request if not complete in 10 seconds
326
- await squared.saveAs(/* archive filename */, { throwErrors: true }).catch(err => console.log(err)); // Will cancel partial archive download
327
- // OR
328
- await squared.copyTo(/* directory */, {/* options */});
329
- await squared.copyTo(/* directory */, { modified: true }); // Can be used with observe
330
- // OR
331
- await squared.appendTo(/* archive location */, {/* options */});
332
-
333
- // Other features
334
-
335
- squared.observe();
336
- // OR
337
- await squared.observeSrc(
338
- "link[rel=stylesheet]", // HTMLElement
339
- (ev, element) => {
340
- squared.reset();
341
- squared.parseDocument().then(() => squared.copyTo("/path/project"));
342
- },
343
- { port: 8080, secure: false, action: "reload" /* "hot" */, expires: "1h" } // squared.json: "observe"
344
- );
345
-
346
- squared.reset(/* projectId */); // Start new "parseDocument" session (optional)
347
- });
348
- </script>
349
- ```
350
-
351
- > [!CAUTION]
352
- > Calling `saveAs` or `copyTo` methods before the images have completely loaded can cause them to be excluded from the generated layout. In these cases you should use the asynchronous `parseDocument` method to set a callback for your commands.
353
-
354
- ### Example: chrome
355
-
356
- Used primarly for developing single page layouts but can also bundle assets using query selector syntax. It is adequate for most projects and gives you the ability to develop your application as a module in place.
357
-
358
- * ES2020
359
-
360
- ```html
361
- <script src="/dist/squared.min.js"></script>
362
- <script src="/dist/squared.base.min.js"></script>
363
- <script src="/dist/chrome.framework.min.js"></script>
364
- <script>
365
- document.addEventListener("DOMContentLoaded", async () => {
366
- squared.setFramework(chrome, {/* settings */});
367
-
368
- await squared.save(); // Uses defaults from settings
369
- // OR
370
- await squared.saveAs(/* archive filename */, {/* options */});
371
- // OR
372
- await squared.copyTo(/* directory */, {/* options */});
373
- // OR
374
- await squared.appendTo(/* archive location */, {/* options */});
375
-
376
- // Observe
377
- await squared.copyTo(/* directory */, { useOriginalHtmlPage: false, observe: /* Same as Android */ | true /* Auto-reload */}).then(() => squared.observe());
378
- });
379
- </script>
380
- ```
381
-
382
- ### Example: vdom
383
-
384
- The most minimal framework possible (*80kb gzipped*) and can be useful when debugging through DevTools. The `lite` version is about half the bundle size and is recommended for most browser applications.
385
-
386
- * ES2018
387
-
388
- ```html
389
- <script src="/dist/squared.min.js"></script>
390
- <script src="/dist/squared.base-dom.min.js"></script>
391
- <script src="/dist/vdom.framework.min.js"></script>
392
- <script>
393
- document.addEventListener("DOMContentLoaded", async () => {
394
- squared.setFramework(vdom, {/* settings */});
395
-
396
- const element = squared.querySelector("body", true /* synchronous */);
397
- // OR
398
- const elements = await squared.querySelectorAll("*");
399
- // OR
400
- const element = squared.fromElement(document.body, true /* synchronous */);
401
- // OR
402
- const elements = await squared.getElementById("content-id").querySelectorAll("*");
403
- });
404
- </script>
405
- ```
406
-
407
- There are **ES2018** minified versions (\*.min.js) and also **ES2018** non-minified versions.
408
-
409
- ## User Settings
410
-
411
- These settings are available in the global variable `squared` to customize your desired output structure. Each framework shares a common set of settings and also a subset of their own settings.
412
-
413
- ### Example: android
414
-
415
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/android.html)
416
-
417
- ```javascript
418
- squared.settings = {
419
- targetAPI: 35,
420
- supportRTL: true,
421
- supportNegativeLeftTop: true,
422
- supportUnicode: ["emoji"], // true | "utf-16" | "emoji"
423
- preloadImages: true,
424
- preloadFonts: true,
425
- preloadLocalFonts: true, // Chromium
426
- preloadCustomElements: true,
427
- enabledSVG: true,
428
- enabledMultiline: true,
429
- enabledViewModel: true,
430
- enabledIncludes: false,
431
- enabledFragment: false,
432
- enabledSubstitute: false,
433
- enabledCompose: false,
434
- dataBindableElements: [], // { selector, attr, expression, namespace?, twoWay? } (see Data Binding section)
435
- includableElements: [], // { selectorStart, selectorEnd, pathname?, filename?, merge?, viewModel? }
436
- substitutableElements: [], // { selector, tag, tagChild?, renderChildren?, autoLayout? }
437
- fragmentableElements: [], // selector | ExtensionFragmentElement
438
- composableElements: [], // selector or property (see Jetpack Compose section)
439
- baseLayoutAsFragment: false | "fragment-name" | ["fragment-name", "fragment-tag"] | { selector, pathname?, filename?, name?, tag? }, // ExtensionFragmentElement
440
- baseLayoutToolsIgnore: "", // Android Studio (e.g. "TooManyViews, HardcodedText")
441
- fontMeasureAdjust: 0.75, // thicker < 0 | thinner > 0 (data-android-font-measure-adjust)
442
- lineHeightAdjust: 1.1, // shorter < 1 | taller > 1 (data-android-line-height-adjust)
443
- preferMaterialDesign: false | "MaterialComponents" | "Material3", // Default is "Material3"
444
- createDownloadableFonts: true,
445
- createElementMap: false,
446
- pierceShadowRoot: true,
447
- adaptStyleMap: true, // Use rendered values for output
448
- lockElementSettings: true,
449
- customizationsBaseAPI: 0, // 0 - All | -1 - None
450
- customizationsBaseAPI: [0, 33, 34], // Multiple
451
- removeDeprecatedAttributes: true, // Remove all
452
- removeDeprecatedAttributes: ["enabled", "singleLine"], // Remove all except "enabled" + "singleLine"
453
- removeUnusedResourceViewId: false,
454
- idNamingStyle: "android",
455
- idNamingStyle: "html", // Use element tagName
456
- idNamingStyle: {
457
- "__default__": "html", // Optional
458
- "DIV": "comments", // HTML is uppercase (comments_1 then comments_2)
459
- "svg": ["vector", 0], // SVG elements areis lowercase (vector_0 then vector_1)
460
- "#text": "text", // Plain text
461
- "::first-letter": "dropcap", // Pseudo element
462
- "main > section": ["content", 1, 2], // content_1 then content_3
463
- "form input[type=submit]": function(node) {
464
- return "submit_" + node.id;
465
- }
466
- },
467
- customizationsOverwritePrivilege: true,
468
- outputMainFileName: "activity_main.xml",
469
- outputFragmentFileName: "fragment_main.xml",
470
- /* Project - parseDocument (first only) */
471
- supportUnicode: "utf-16",
472
- resourceQualifier: "", // "land" -> "res/layout-land" | "port" -> "res/layout-port" (appended to every "res" folder)
473
- resourceSystemColors: {
474
- "system_accent1_100": "white", // Will be converted to ARGB
475
- "system_accent1_200": ['#ff0000', 0.75], // opacity
476
- "system_accent1_300": squared.lib.color.parseColor("#000", 1)
477
- },
478
- manifestPackage: "", // OR: RequestData<{ namespace: "android.application.id" }>
479
- manifestLabelAppName: "android",
480
- manifestThemeName: "AppTheme",
481
- manifestParentThemeName: "Theme.AppCompat.Light.NoActionBar",
482
- manifestActivityName: ".MainActivity",
483
- outputDocumentEditing: true,
484
- outputDocumentCSS: [], // CSS properties to be processed at server (e.g. "boxShadow")
485
- outputDirectory: "app/src/main",
486
- createManifest: false, // Update AndroidManifest.xml
487
- createBuildDependencies: false | "ktx" | "baseline-profile" | ["ktx", "baseline-profile"], // Update build.gradle
488
-
489
- // Not customizable with parseDocument
490
- builtInExtensions: [
491
- "squared.accessibility",
492
- "android.delegate.background",
493
- "android.delegate.negative-x",
494
- "android.delegate.positive-x",
495
- "android.delegate.max-width-height",
496
- "android.delegate.percent",
497
- "android.delegate.content",
498
- "android.delegate.scrollbar",
499
- "android.delegate.radiogroup",
500
- "android.delegate.multiline",
501
- "squared.relative",
502
- "squared.css-grid",
503
- "squared.flexbox",
504
- "squared.table",
505
- "squared.column",
506
- "squared.list",
507
- "squared.grid",
508
- "squared.sprite",
509
- "squared.whitespace",
510
- "android.resource.background",
511
- "android.resource.svg",
512
- "android.resource.strings",
513
- "android.resource.fonts",
514
- "android.resource.dimens",
515
- "android.resource.styles",
516
- "android.resource.data",
517
-
518
- /* EXCLUDED (breaks layout) */
519
- "android.resource.includes", // enabledIncludes
520
- "android.substitute", // enabledSubstitute
521
- "android.resource.fragment", // enabledFragment
522
- "jetpack.compose.view" // enabledCompose
523
- ],
524
- compressImages: false, // TinyPNG API Key <https://tinypng.com/developers>
525
- compressImages: "****************", // API key
526
- compressImages: [{ plugin: "imagemin-pngquant", format: "png", options: { quality: [0.6, 0.8] } }], // v5.5
527
- convertImages: "", // png | jpeg | webp | gif | bmp
528
- showAttributes: true,
529
- showAttributes: {
530
- "hyphenationFrequency": "full", // Replace all ("android" is the default namespace)
531
- "android:fontFeatureSettings": null, // Delete all
532
- "app:menu": [
533
- "@menu/menu_1", "@menu/menu_2", // Replace with "@menu/menu_2" when value is "@menu/menu_1"
534
- "@menu/menu_3", null // Delete attribute when value is "@menu/menu_3"
535
- ],
536
- /* OR */
537
- "app:menu": {
538
- "@menu/menu_1": "@menu/menu_2",
539
- "@menu/menu_3": null
540
- }
541
- },
542
- showComments: false | ["boxShadow"] | { self: ["boxShadow"], nextSibling: ["marginBottom"], previousSibling: ["marginTop"], parent: ["position", "top", "left"] }, // TODO in layout.xml
543
- showComments: { include: { tagName: true | ["button"], attributes: true | ["style"], dataset: false, bounds: true }, self: ["boxShadow", ".className"] },
544
- showErrorMessages: false,
545
- convertPixels: "dp", // "sp" | "pt" | "in" | "mm"
546
- convertLineHeight: "sp", // "dp" | "pt" | "in" | "mm"
547
- convertEntities: ["numeric"],
548
- convertEntities: ["codepoints", {/* JSON (last) */}], // https://html.spec.whatwg.org/entities.json
549
- insertSpaces: 4,
550
- outputDocumentHandler: "android",
551
- outputEmptyCopyDirectory: false, // Sub directories within target directory (OR: RequestData<{ emptyDir: false }>)
552
- outputSummaryModal: false | "path/summary.css" | ".status-4 { color: purple; }",
553
- outputTasks: {
554
- "**/drawable/*.xml": { handler: "gulp", task: "minify" }
555
- },
556
- outputWatch: {
557
- "**/drawable/*.png": true,
558
- "**/drawable/*.jpg": { interval: 1000, expires: "2h" }
559
- },
560
- outputArchiveName: "android-xml",
561
- outputArchiveFormat: "zip", // tar | 7z | gz
562
- outputArchiveCache: false // Downloadable URL in ResponseData<downloadUrl>
563
- };
564
-
565
- // Optional
566
- squared.settings = {
567
- resolutionDPI: 160, // 320dpi = 2560x1600
568
- resolutionScreenWidth: 1280,
569
- resolutionScreenHeight: 800,
570
- framesPerSecond: 60,
571
- useShapeGeometryBox: true, // Bounding box uses native SVG method getBbox
572
- formatUUID: "8-4-4-4-12", // UUID: 8-4-[12345]3-[89ab]3-12
573
- formatDictionary: "0123456789abcdef",
574
- outputConfigName: "sqd.config",
575
- observePort: 8080,
576
- observeSecurePort: 8443,
577
- observeExpires: "1h", // Server defaults will be used
578
- broadcastPort: 3080,
579
- broadcastSecurePort: 3443
580
- };
581
- ```
582
-
583
- ### Example: chrome
584
-
585
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/chrome.html)
586
-
587
- ```javascript
588
- squared.settings = {
589
- preloadImages: false,
590
- preloadFonts: false,
591
- preloadLocalFonts: false,
592
- preloadCustomElements: false,
593
- excludePlainText: true,
594
- createElementMap: true,
595
- pierceShadowRoot: true,
596
- adaptStyleMap: false,
597
- builtInExtensions: [],
598
- showErrorMessages: false,
599
- webSocketPort: 80,
600
- webSocketSecurePort: 443,
601
- outputDocumentHandler: "chrome",
602
- outputEmptyCopyDirectory: false,
603
- outputSummaryModal: false,
604
- outputTasks: {
605
- "*.js": [{ handler: "gulp", task: "minify" }, { handler: "gulp", task: "beautify" }]
606
- },
607
- outputWatch: { "*": true },
608
- outputArchiveName: "chrome-data",
609
- outputArchiveFormat: "zip",
610
- outputArchiveCache: false
611
- };
612
-
613
- // Optional (Same as Android)
614
-
615
- ```
616
-
617
- ### Example: vdom
618
-
619
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/vdom.html)
620
-
621
- ```javascript
622
- squared.settings = {
623
- createElementMap: true,
624
- pierceShadowRoot: false,
625
- adaptStyleMap: false,
626
- builtInExtensions: [],
627
- showErrorMessages: false
628
- };
629
- ```
630
-
631
- ## Local Storage
632
-
633
- Custom named user settings per framework can be saved to local storage as JSON and reused across all pages in the same domain. Extensions are configured using the same procedure.
634
-
635
- ```javascript
636
- // Save
637
- squared.setFramework(android, { compressImages: true }, "android-example");
638
-
639
- // Load
640
- squared.setFramework(android, "android-example");
641
- ```
642
-
643
- ```javascript
644
- // Save
645
- await squared.copyTo("/path/project", {/* options will be saved */}, "copy-example", true); // Will overwrite and not merge with previously saved settings
646
-
647
- // Load
648
- await squared.copyTo("/path/project", {/* takes precedence */}, "http://localhost:3000/copy-to/base-config.json"); // Object.assign({ base-config.json }, options)
649
- await squared.copyTo("/path/project", {/* takes precedence */}, "copy-example"); // Object.assign({ copy-example }, options)
650
-
651
- await squared.copyTo("/path/project", "http://localhost:3000/copy-to/base-config.json"); // options = { base-config.json }
652
- await squared.copyTo("/path/project", "copy-example"); // options = { copy_example }
653
- ```
654
-
655
- ## Public Properties and Methods
656
-
657
- - [Read the Docs](https://squared.readthedocs.io/en/latest/methods/squared.html)
658
-
659
- ```javascript
660
- .settings // See user preferences section
661
-
662
- setFramework(app: {}, options?: PlainObject, setting?: string, cache?: boolean) // Install application interpreter
663
- setFramework(app: {}, loadName: string, cache?: boolean) // Load settings from local storage
664
-
665
- // http - hostname(:port)? | https - hostname:443
666
- setHostname(value: string /* http(s)://hostname(:port) */) // Use another cors-enabled server for processing files (--cors <origin>)
667
-
668
- setEndpoint(name: string, value: string) // Set alternate pathname for API v1 functions (ASSETS_COPY | ASSETS_ARCHIVE | LOADER_DATA | THREADS_KILL | WEBSOCKET_OBSERVE)
669
- setLocalAddress(...values: (string | URL | Location)[]) // Additional hostnames which are interpreted as localhost (e.g. http://127.0.0.1)
670
-
671
- prefetch(type: "css" | "javascript" | "image" | "svg", all?: boolean, ...targets: unknown[]) // Cross-origin support for CSS
672
-
673
- parseDocument(...elements: (HTMLElement | string | ElementSettings)[]) // See installation section (Promise)
674
- parseDocumentSync(...elements: (HTMLElement | string | ElementSettings)[]) // Skips preloadImages and preloadFonts (synchronous)
675
-
676
- latest(count?: number) // Most recent parseDocument session ids (1 newest / -1 oldest: string, other: string[])
677
-
678
- auth(token: string) // Set JWT authorization token for all requests
679
-
680
- save(projectId?: string) // Save current session to a new archive using default settings
681
- save(projectId?: string, broadcastId?: string)
682
- save(projectId?: string, timeout?: number)
683
-
684
- close(projectId?: string) // Close current session
685
- reset(projectId?: string) // Clear cache and reopen new session
686
- clear() // Clear all data stored in memory
687
-
688
- toString() // Current framework loaded
689
- toString(projectId: string) // await squared.close(projectId) (required)
690
-
691
- add(...names: (string | Extension | ExtensionRequestObject)[]) // See extension configuration section
692
- remove(...names: (string | Extension)[]) // Remove extensions by namespace or control
693
- get(...names: string[]) // Retrieve extensions by namespace
694
- attr(name: string | Extension, attrName: string, value?: unknown) // Set or get extension options attribute value
695
- apply(name: string | Extension, options: PlainObject, setting?: string) // See extension configuration section
696
-
697
- extend(functionMap: {}, framework?: /* 0 - ALL | 1 - vdom | 2 - android | 4 - chrome */) // Add extension functions and properties to Node prototype
698
-
699
- observe(value?: boolean | MutationObserverInit) // Start after DOM and third-party libraries initialization
700
- broadcast(callback: BroadcastMessageCallback, options: FileBroadcastOptions | string) // Redirect stdout messages to DevTools console
701
-
702
- // Promise (Recommended "cache": createElementMap - true)
703
-
704
- getElementById(value: string, sync?: boolean, cache?: boolean) // sync - false | cache - true (default)
705
- querySelector(value: string, sync?: boolean, cache?: boolean)
706
- querySelectorAll(value: string, sync?: boolean, cache?: boolean)
707
-
708
- fromElement(element: HTMLElement | string, sync?: boolean, cache?: boolean) // sync - false | cache - false (default)
709
- fromNode(node: Node, sync?: boolean, cache?: boolean)
710
- findDocumentNode(element: HTMLElement | string /* querySelector | elementId | controlId */, all?: boolean) // Use before saving to modify internal Node attributes
711
-
712
- observeSrc(element: HTMLElement | string /* querySelector */, callback: WebSocketMessageChange, options?: FileObserveOptions) // Can be used to watch any element with externally hosted files (src/href)
713
- observeSrc(element: HTMLElement | string, options: FileObserveOptions) // Uses location.reload (reload - true)
714
- ```
715
-
716
- Packaging methods will return a Promise and requires a squared-express installation. These features are not supported when the framework is VDOM.
717
-
718
- ```javascript
719
- saveAs(filename: string, options?: {}, setting?: string, overwrite?: boolean) // Save current session as a new archive
720
- saveFiles(filename: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from FileAsset[]
721
-
722
- // Required (local archives): --disk-read | --unc-read | --access-all (command-line)
723
-
724
- appendTo(pathname: string, options?: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and current session
725
- appendFiles(pathname: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and FileAsset[]
726
-
727
- // Required (all): --disk-write | --unc-write | --access-all (command-line)
728
-
729
- copyTo(pathname: string | string[], options?: {}, setting?: string, overwrite?: boolean) // Copy current session to local
730
- copyFiles(pathname: string | string[], options: {}, setting?: string, overwrite?: boolean) // Copy FileAsset[] to local
731
-
732
- kill(pid: number, timeout?: number) // Use -1 or options.pid (set by system) + seconds
733
- kill(timeout: string)
734
- kill() // Terminate previous request
735
- kill(0) // By username (auth required)
736
- kill(-1, 10) // Terminate previous request in 10 seconds
737
- kill(NaN, 10) // Terminate in 10 seconds (next request) ("timeout" is required)
738
- kill("10s") // Only "s" + "ms" (next request)
739
- ```
740
-
741
- ## Extending Node object
742
-
743
- You can add functions and initial variables to the Node object including overwriting preexisting class definitions per framework. Accessor properties are also supported using the *get/set* object syntax.
744
-
745
- ```javascript
746
- squared.extend({
747
- _id: 1,
748
- altId: {
749
- get() {
750
- return this._id;
751
- },
752
- set(value) {
753
- this._id += value;
754
- }
755
- },
756
- customId: {
757
- value: 2,
758
- configurable: false,
759
- enumerable: false
760
- },
761
- addEvent(eventName, callback) {
762
- this.element.addEventListener(eventName, callback);
59
+ # REPO_ROOT = /workspaces | Dir.pwd
60
+ # rake = /workspaces/Rakefile |
61
+ # REPO_HOME = /workspaces/squared | main: "squared"
62
+
63
+ # pathname = /workspaces/pathname
64
+ # optparse = /workspaces/optparse
65
+ # log = /workspaces/logger
66
+ # emc = /workspaces/e-mc
67
+ # pir = /workspaces/pi-r
68
+ # pir2 = /workspaces/pi-r2
69
+ # squared = /workspaces/squared
70
+ # cli = /workspaces/squared/publish/sqd-cli
71
+ # sqd-serve = /workspaces/squared/publish/sqd-serve
72
+ # sqd = /workspaces/squared/sqd
73
+
74
+ Workspace::Application
75
+ .new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
76
+ .banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
77
+ .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
78
+ .run("rake install", ref: :ruby)
79
+ .depend(false, group: "default")
80
+ .clean("rake clean", group: "default")
81
+ .clean(["build/"], group: "app")
82
+ .log({ file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app")
83
+ .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", env: { # Ruby (with C extensions)
84
+ "CFLAGS" => "-fPIC -O1"
85
+ })
86
+ .add("optparse", doc: "rake rdoc", gemspec: "optparse.gemspec", group: "default") # Uses bundler/gem_tasks (without C extensions)
87
+ .add("logger", copy: { from: "lib", glob: "**/*.rb", into: "~/.rvm/gems/ruby-3.4.0/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true | "rvm" | "rbenv" | "asdf" | "bundler"
88
+ .add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
89
+ .add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
90
+ .add("pi-r2", "pir2", copy: { from: :npm, also: %i[squared express], files: ["LICENSE", ["README.md.ruby", "README.md"]] }) # Uses dist files from NPM package spec
91
+ .add("squared", init: 'pnpm', script: ["build:stage1", "build:stage2"], group: "app") do # Use pnpm/yarn/berry for depend + Copy target (main)
92
+ # Repo (global)
93
+ as(:run, "build:dev", "dev") # npm run build:dev -> npm run dev
94
+ as(:run, { "build:dev": "dev", "build:prod": "prod" })
95
+
96
+ add("publish/sqd-cli", "cli", exclude: [:git]) # rake cli:build
97
+ add("publish/sqd-serve") # rake sqd-serve:build
98
+ add("publish/sqd-admin", group: "sqd", exclude: [:base])
99
+ # OR
100
+ with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
101
+ # OR
102
+ add(["publish/sqd-cli", "publish/sqd-serve"], true, exclude: [:base]) # rake squared:sqd-serve:build
103
+
104
+ # Git
105
+ revbuild(include: %w[src/ framework/ types/]) # Synchronous is recommended
106
+ end
107
+ .add("squared/sqd", exclude: :git, pass: [:node, "checkout", "bump"]) do # Skip initialize(:node) + squared:checkout:* + squared:bump:*
108
+ variable_set :script, "build:sqd" # Override detection
109
+ variable_set :depend, false
110
+ variable_set :clean, ["build/sqd/"]
111
+ end
112
+ .with(:docker, only: ["build", "compose"]) do
113
+ .add("squared", "docker", file: "Dockerfile", context: ".", tag: "latest", registry: "localhost:5000", username: "squared",
114
+ args: "--ssh=default",
115
+ secrets: ["id=github,env=GITHUB_TOKEN"],
116
+ mounts: ["src=.,dst=/project,ro,bind-propagation=rshared"]) do # Docker
117
+ series(:clean) do # run | depend | doc | lint | test | copy | clean
118
+ File.read(basepath("docker-bake.hcl"))
119
+ .scan(/\btags\s+=\s+\["([^"]+)"\]/)
120
+ .each { |val| image(:rm, tag: val.first) }
121
+ end
122
+ end
123
+ end
124
+ .pass("pull", group: "default") { test? || doc? } # rake pathname:pull | rake optparse:pull
125
+ .style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
126
+ .build(default: "build", parallel: ["pull", "fetch", "rebase", "archive", "clean", /^outdated:/], pass: ["publish"]) do |workspace|
127
+ workspace
128
+ .enable_aixterm
129
+ .style({
130
+ banner: ["bright_cyan", "bold", "bright_black!"],
131
+ border: "bright_white"
132
+ })
133
+ end
134
+
135
+ # default = /workspaces/ruby/*
136
+ # pathname = /workspaces/ruby/pathname
137
+ # optparse = /workspaces/ruby/optparse
138
+ # logger = /workspaces/ruby/logger
139
+ # android = /workspaces/android-docs
140
+ # chrome = /workspaces/chrome-docs
141
+
142
+ Workspace::Application
143
+ .new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
144
+ .group("ruby", "default", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
145
+ pathname: {
146
+ run: "rake compile" # rake rb:pathname:build
763
147
  }
764
- });
765
- squared.setFramework(vdom);
766
-
767
- const body = await squared.fromElement(document.body);
768
- body.altId = 2; // body.altId: 3
769
- body.addEvent("click", function (ev) {
770
- this.classList.toggle("example");
771
- });
772
- ```
773
-
774
- ## Forwarding Request
775
-
776
- Using another identical remote server to build the project when performing a `saveAs` or `copyTo` request can be achieved by changing only the origin address.
777
-
778
- ```javascript
779
- squared.setHostname("http://hostname:8000");
780
- // OR
781
- squared.setHostname(); // Reset to window.location (e.g. localhost:3000)
782
-
783
- await squared.saveAs("chrome.zip"); // Current browser
784
- // OR
785
- await squared.copyTo("/path/project"); // Remote server
786
- ```
787
-
788
- ## Broadcasting
789
-
790
- Console messages (stdout) can be sent to the browser console instead through DevTools.
791
-
792
- ```javascript
793
- squared.broadcast(result => { console.log(result.value); }, "111-111-111"); // System messages from squared-express
794
- squared.broadcast(result => { console.log(result.value); }, "222-222-222"); // Messages from "project-1" project
795
- squared.broadcast(result => { console.log(result.value); }, { socketId: "333-333-333", socketKey: "socket_id" }); // Messages sent from another channel (default is "socketId")
796
-
797
- await squared.copyTo("/path/project/project-1", {
798
- projectId: "project-1",
799
- log: { useColor: true }, // Chromium
800
- broadcastId: "222-222-222" // Specific use alias for "socketId"
801
- });
802
- ```
803
-
804
- ## Extension Configuration
805
-
806
- Layout rendering can be customized using extensions as the program was built to be nearly completely modular. Some of the common layouts already have built-in extensions which you can load or unload based on your preference.
807
-
808
- ```typescript
809
- // Create an extension
810
- class Sample extends squared.base.Extension {
811
- options = {
812
- attributeName: [];
813
- };
814
-
815
- constructor(name, framework = 0, options = {}) { // 0 - ALL | 1 - vdom | 2 - android | 4 - chrome (framework)
816
- super(name, framework, options);
817
- }
818
-
819
- processNode(node: NodeUI) {
820
- const data = this.project.get(node.element, node.localSettings.projectId);
821
- if (data) {
822
- node.each((child, index) => child.element.title = data[index]);
823
- }
148
+ })
149
+ .with(:python, editable: false) do # ref=Symbol | group=String
150
+ banner([:name, ": ", :version], "path") # chrome-docs: 0.1.0 | /workspaces/chrome-docs
151
+ doc("make html") # rake rb:doc:python
152
+ run(false) # rake rb:build:python (disabled)
153
+ exclude(%i[base git]) # Project::Git.ref (superclass)
154
+ add("android-docs", "android") # rake rb:android:doc
155
+ add("chrome-docs", "chrome") # rake rb:chrome:doc
156
+ end
157
+ .style("inline", "bold")
158
+ .build
159
+ ```
160
+
161
+ **NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
162
+
163
+ ### Archive
164
+
165
+ ```ruby
166
+ # HEADERS={"Authorization":"Bearer RANDOM-TOKEN"} | hash/json
167
+ # ZIP_DEPTH=0 | default=1
168
+ # TAR_DEPTH=0 | TAR_DEPTH_SQUARED
169
+ # UNPACK_FORCE=1 | Remove target directory
170
+
171
+ Workspace::Application
172
+ .new(main: "squared")
173
+ .with(:python) do
174
+ add("android-docs", "android", archive: "https://github.com/anpham6/android-docs/archive/refs/tags/v0.3.0.zip")
175
+ add("chrome-docs", "chrome", archive: {
176
+ uri: "https://github.com/anpham6/chrome-docs/archive/refs/tags/v0.5.0.tar.gz", # URI.open (required)
177
+ digest: "e3d55d2004d4770dd663254c9272dc3baad0d57a5bd14ca767de6546cdf14680", # SHA1 | SHA256 | SHA384 | SHA512 | MD5
178
+ digest: "rmd160:47b7790a511eed675fec1a3e742845fef058799b", # RMD160
179
+ ext: "tar.gz", # zip | tar | tar.gz | tgz | tar.xz | txz | 7z
180
+ depth: 1, # nested directories (e.g. --strip-components)
181
+ headers: { # URI.open
182
+ "Authorization" => "Bearer RANDOM-TOKEN"
183
+ }
184
+ })
185
+ end
186
+ .add("squared", release: "https://github.com/anpham6/squared/archive/refs/tags/??") # squared:unpack:zip[v5.4.0,/tmp/squared]
187
+ end
188
+ ```
189
+
190
+ ### Clone
191
+
192
+ The task is only active when the project directory is empty or does not exist.
193
+
194
+ ```ruby
195
+ Workspace::Application
196
+ .new(main: "squared")
197
+ .git(
198
+ "emc": "https://github.com/anpham6/e-mc", # rake emc:clone
199
+ "pir": { # rake pir:clone
200
+ uri: "https://github.com/anpham6/pi-r", #
201
+ options: { #
202
+ "origin": "github", # --origin=github
203
+ "recurse-submodules": false, # --no-recurse-submodules
204
+ "shallow-exclude": ["v0.0.1", "v0.0.2"] # --shallow-exclude=v0.0.1 --shallow-exclude=v0.0.2
205
+ }
824
206
  }
825
- }
826
-
827
- // Install an extension
828
- const sample = new Sample("widget.example.com", 0, {/* Same as configure */});
829
- squared.add(sample);
830
- // OR
831
- squared.add([sample, {/* config */}]);
832
-
833
- // Configure an extension
834
- squared.attr("widget.example.com", "attributeName", ["width", "height"]); // typeof is enforced and will only set existing attributes
835
-
836
- // Add project data
837
- const ext = squared.get("widget.example.com");
838
-
839
- ext.project.set(element, await fetch(url?id=1)); // Map interface with optional "projectId" argument
840
- ext.project.set(element, await fetch(url?id=2), "project-1");
841
-
842
- const data = ext.project.get(element, "project-2"); // Returns data from default project (id=1)
843
- ```
844
-
845
- Some extensions have a few settings which can be configured. The default settings usually achieve the best overall rendering accuracy without noticeably affecting performance.
846
-
847
- ## ANDROID
848
-
849
- ### Public Methods
850
-
851
- - [Read the Docs](https://squared.readthedocs.io/en/latest/methods/android.html)
852
-
853
- ```javascript
854
- android.setViewModel(data: {}, sessionId?: string) // Object data for layout bindings
855
- android.setViewModelByProject(data: {}, projectId?: string)
856
- android.removeObserver(element: HTMLElement) // Disconnect an observed element from "parseDocument"
857
- android.addXmlNs(name: string, uri: string) // Add global namespaces for third-party controls
858
- android.addDependency(group: string, name: string, version?: string, type?: number) // Add application dependency implementation (build.gradle)
859
- android.addDependencyByProject(projectId: string, group: string, name: string, version?: string, type?: number) // DEPENDENCY_TYPE: 0 - implementation 1 - api 2 - compileOnly 3 - compileOnlyApi 4 - runtimeOnly 5 - testImplementation 8 - androidTestImplementation
860
- android.customize(build: number, tagNameOrWidget: string, options: {}) // Global attributes applied to specific views
861
- android.loadCustomizations(name: string) // Load customizations from Local Storage
862
- android.saveCustomizations(name: string) // Save "customize" data into Local Storage (includes xmlns)
863
- android.resetCustomizations() // All session customizations are deleted
864
- android.addFontProvider(authority: string, package: string, certs: string[], webFonts: string | {}) // Add additional Web fonts (Google Fonts already included)
865
- android.setResolutionByDeviceName(value: string) // Settings prefixed with "resolution" (e.g. Pixel C)
866
- android.getLocalSettings() // Modify controller styles and parsing rules
867
- ```
868
-
869
- ```javascript
870
- // NOTE: squared.settings.targetAPI is always parsed (Except: customizationsBaseAPI = -1)
871
-
872
- android.customize(android.lib.constant.BUILD_VERSION.ALL /* 0 */, "Button", {
873
- android: {
874
- minWidth: "35px",
875
- minHeight: "25px"
207
+ )
208
+ .git("squared", "/path/to/squared", options: { local: true }) # Relative paths resolve from workspace root
209
+ .git(
210
+ {
211
+ emc: { uri: "e-mc", options: { "depth": 2 } }, # https://github.com/anpham6/e-mc
212
+ pir: "pi-r" # Maps task alias to repository folder
876
213
  },
877
- "_": { // Non-namespaced attributes
878
- style: "@style/Widget.Material3.Button.TextButton"
214
+ base: "https://github.com/anpham6", # Required
215
+ repo: ["squared", "android-docs", "chrome-docs"], # https://github.com/anpham6/squared
216
+ options: { # Only "repo"
217
+ "depth": 1,
218
+ "quiet": true
879
219
  }
880
- });
881
-
882
- android.customize(android.lib.constant.BUILD_VERSION.KITKAT /* 19 */, "svg", {
883
- android: {
884
- "[src]": "app:srcCompat" // Change namespace to "app"
885
- }
886
- });
887
-
888
- // Local Storage
889
- android.saveCustomizations("customize-example"); // Save at least once in one layout
890
-
891
- android.loadCustomizations("customize-example"); // Load in any other layout
220
+ )
221
+ .with(:node) do # rake clone:node
222
+ add("e-mc", "emc") # https://github.com/anpham6/e-mc
223
+ add("pi-r", "pir") # https://github.com/anpham6/pi-r
224
+ add("squared") # https://github.com/anpham6/squared
225
+ end
226
+ .with(:python) do # rake clone:python
227
+ add("android-docs")
228
+ add("chrome-docs") do
229
+ revbuild(include: "source/", exclude: ["source/conf.py"]) # Limit files being watched
230
+ end
231
+ end
232
+ .git("https://github.com/anpham6", ["emc", "pir"]) # Targets any defined project
233
+ .git("https://github.com/anpham6", cache: true) # Uses already defined root projects + revbuild
234
+ .revbuild # Enables task revbuild (squared.revb)
235
+ .revbuild(file: "../build.json") # $ROOT/build.json
236
+ .build(parallel: ["clone"]) # rake clone + rake clone:sync
237
+ ```
238
+
239
+ ### Build
240
+
241
+ #### Chain
242
+
243
+ There has to be at least one project which uses the ``step`` attribute. Other placement attributes are ignored.
244
+
245
+ **NOTE**: Projects can only reference non-global namespaced tasks. (e.g. with ":")
246
+
247
+ ```ruby
248
+ Workspace::Application
249
+ .new
250
+ .with(:node) do
251
+ add("e-mc", "emc") do
252
+ chain "all", "clean", step: 1 # Required
253
+ chain "all", "build", step: 2
254
+ end
255
+ add("pi-r", "pir") do
256
+ chain "all", "build", after: "emc:build" # step: 3
257
+ end
258
+ add("pi-r2", "pir2") do
259
+ chain "all", "build", before: "squared" # step: 3
260
+ end
261
+ add("squared-express", "express") do
262
+ chain "all", "clean", with: "emc" # step: 1
263
+ chain "all", "build", with: "pir" # step: 3
264
+ end
265
+ add("squared") do
266
+ revbuild(include: %w[src/ framework/ types/]) # Git revision build command (optional)
267
+ chain "all", "revbuild", after: "express:build" # step: 4
268
+ chain "publish", "bump:patch", "publish:latest", step: 0, sync: true # rake publish -> squared:bump:patch -> squared:publish:latest
269
+ end
270
+ end
271
+ .with(:python) do
272
+ doc("make html")
273
+ add("android-docs") do
274
+ chain "all", "doc", with: "squared", after: "squared" # step: 4
275
+ end
276
+ add("chrome-docs") do
277
+ chain "all", "doc", with: "squared", before: "squared:revbuild" # Same
278
+ end
279
+ end
280
+ .chain "all", "status", with: "squared", after: "android-docs" # Global tasks (e.g. without ":")
281
+ .build
892
282
  ```
893
283
 
894
- ```javascript
895
- android.addXmlNs("tools", "http://schemas.android.com/tools");
896
-
897
- android.customize(16 /* Jelly Bean */, "ImageView", {
898
- tools: {
899
- ignore: "ContentDescription",
900
- targetApi: "16"
901
- }
902
- });
284
+ ```sh
285
+ rake all # all[1-3-4]
286
+ rake all:print
287
+ ```
288
+
289
+ Threaded is the default when there are two or more tasks. Using ``with`` and either **before** or **after** will create a synchronous group.
290
+
291
+ * Step 1: emc:clean + express:clean (thread)
292
+ * Step 2: emc:build (sync)
293
+ * Step 3: pir:build + express:build + pir2:build (thread)
294
+ * Step 4: chrome-docs:doc + squared:revbuild + android-docs:doc + status (sync)
295
+
296
+ #### Graph
297
+
298
+ ```ruby
299
+ Workspace::Application
300
+ .new(main: "squared")
301
+ .graph(["depend"], ref: :git) # Optional
302
+ .with(:python) do
303
+ doc(windows? ? ".\make.bat html" : "make html") # rake android-docs:doc | rake doc:python
304
+ add("android-docs", "android", venv: "/home/user/.venv") # rake android-docs:depend
305
+ add("chrome-docs", "chrome", graph: "android", venv: %w[.venv --clear]) do # /workspaces/chrome-docs/.venv
306
+ variable_set :dependindex, 1 # Use Poetry for dependencies (optional)
307
+ end
308
+ end
309
+ .with(:node) do
310
+ graph(["build", "copy"], on: { # Overrides "git"
311
+ first: proc { puts "1" },
312
+ last: proc { puts "2" }
313
+ })
314
+ script("build:dev") # npm run build:dev
315
+ # OR
316
+ run([nil, "build:dev", { "PATH" => "~/.bin" }, "--workspace", "--silent"]) # PATH="~/.bin" npm run build:dev --workspace -- --silent
317
+ # OR
318
+ run({ # Same
319
+ script: "build:dev", #
320
+ env: { "PATH" => "~/.bin" }, #
321
+ opts: "--workspace", #
322
+ args: "--silent" #
323
+ })
324
+
325
+ add("e-mc", "emc") do
326
+ first("build", "emc:clean", "emc:depend") # rake emc:clean && rake emc:depend && rake emc:build && echo "123"
327
+ last("build", out: "123") { |out: nil| puts out } #
328
+ error("build") { |err: nil| log.debug err } #
329
+ end
330
+ add("pi-r", "pir", graph: "emc", first: {
331
+ build: proc { puts self.name } # puts "pir"
332
+ })
333
+ add("squared-express", "express", graph: "pir")
334
+ add("squared", graph: ["chrome", "express"]) do
335
+ first("git:ls-files") { puts "1" } # skipped
336
+ first("git:ls-files", override: true) { puts "2" } # puts "2"
337
+ last("git:ls-files") { puts workspace.root } # puts "/workspaces"
338
+ end
339
+ end
340
+ .with(:ruby) do
341
+ run("gem build") # gem build
342
+ # OR
343
+ run("gem build", on: { first: -> { p "2" }, last: -> { p "4" } }) do # run | depend | graph | clean | doc | lint | test
344
+ p "1"
345
+ end
346
+ # OR
347
+ run(on: { first: -> { p "pass" }, last: -> { p "pass" } }) do
348
+ p "1"
349
+ end
350
+ # OR
351
+ run(["gem build", "--force", { "RUBY_VERSION" => "3.4.0" }]) # RUBY_VERSION="3.4.0" gem build --force
352
+ # OR
353
+ run({ #
354
+ command: "gem build", # RUBY_VERSION="3.4.0" gem build --silent --force
355
+ opts: "--force", # composable
356
+ env: { "PATH" => "~/.bin" }, #
357
+ args: { silent: true } #
358
+ })
359
+ # OR
360
+ run(["gem pristine", ["gem build", "gem cleanup"], nil, "--debug"]) # gem pristine --debug && gem build --debug && gem cleanup --debug
361
+ #
362
+ # All commands are either Array or Hash
363
+ #
364
+ run([ # PATH="~/.bin" GEM_HOME="~/.gems/ruby-3.4.0" (merged)
365
+ ["gem pristine", "--all", { "PATH" => "~/.bin" }, "--silent"], # gem pristine --silent --all
366
+ ["gem build", { strict: true }, { "GEM_HOME" => "~/.gems/ruby-3.4.0" }] # gem build --strict
367
+ ]) #
368
+ # OR
369
+ run([ # Same
370
+ { #
371
+ env: { "PATH" => "~/.bin" }, #
372
+ command: "gem pristine", #
373
+ opts: "--all", args: "--silent" #
374
+ }, #
375
+ { #
376
+ env: { "GEM_HOME" => "~/.gems/ruby-3.4.0" }, #
377
+ command: "gem build", #
378
+ opts: { strict: true } #
379
+ } #
380
+ ])
381
+
382
+ add("pathname", test: ["rake test", { jobs: ENV["RAKE_JOBS"] }]) # rake test --jobs 4
383
+ add("fileutils", graph: "pathname")
384
+ add("optparse", run: "gem build", env: { "PATH" => "~/.bin" }, opts: "-v") # PATH="~/.bin" gem build -v
385
+ add("rake", graph: ["fileutils", "optparse"])
386
+ banner(command: false) # Always hide banner
387
+ end
388
+ .build
903
389
  ```
904
390
 
905
- ### Static Methods
906
-
907
- Project resources can include additional values that are required during compilation. TypeScript definitions are available in the `types/android` directory.
391
+ ```sh
392
+ rake pir:graph # emc + pir
393
+ rake express:graph # emc + pir + express
394
+ rake chrome:graph # android + chrome
395
+ rake graph:python # same
396
+ rake squared:graph # android + chrome + emc + pir + express + squared
397
+ rake graph:node # same
398
+ rake rake:graph # pathname + fileutils + optparse + rake
399
+ rake graph:ruby # same
400
+ rake graph # graph:node + graph:ruby
908
401
 
909
- ```javascript
910
- squared.parseDocument().then(node => {
911
- const resourceId = node.localSettings.resourceId;
912
- android.base.Resource.addString(resourceId, value, /* name */);
913
- android.base.Resource.addArray(resourceId, name, items);
914
- android.base.Resource.addColor(resourceId, color);
915
- android.base.Resource.addDimen(resourceId, name, value);
916
- android.base.Resource.addTheme(resourceId, theme);
917
- squared.save();
918
- });
402
+ rake squared:graph:run[express,pir] # emc + pir + express + squared
403
+ rake squared:graph:run[node,-emc] # pir + express + squared
919
404
  ```
920
405
 
921
- ### Data Binding
922
-
923
- View model data can be applied to most HTML elements using the dataset attribute. Different view models can be used for every `parseDocument` session.
924
-
925
- Leaving the `sessionId` empty uses the default view model which is searched last for all projects when attempting a bind.
926
-
927
- ```javascript
928
- // NOTE: latest(undefined = 1): string (1: most recent sessionId | -1: first sessionId)
929
-
930
- squared.parseDocument("id-1", "id-2", "id-3").then(nodes => {
931
- const sessions = squared.latest(2); // ["00001", "00002", "00003"] => ["00002", "00003"]
932
- android.setViewModel(
933
- {
934
- import: ["java.util.Map", "java.util.List"],
935
- variable: [
936
- { name: "user", type: "com.example.User" },
937
- { name: "list", type: "List&lt;String>" },
938
- { name: "map", type: "Map&lt;String, String>" },
939
- { name: "index", type: "int" },
940
- { name: "key", type: "String" }
941
- ]
942
- },
943
- sessions[0] // nodes[1].sessionId
944
- );
945
- android.setViewModel(
946
- {
947
- import: ["java.util.Map"],
948
- variable: [
949
- { name: "map", type: "Map&lt;String, String>" }
950
- ]
951
- },
952
- sessions[1] // nodes[2].sessionId
953
- );
954
- });
955
-
956
- squared.parseDocument({
957
- element: "main",
958
- enabledViewModel: true,
959
- dataBindableElements: [
960
- {
961
- selector: "#first_name",
962
- namespace: "android", // "android" is default
963
- attr: "text",
964
- expression: "user.firstName"
965
- },
966
- {
967
- selector: "#last_name",
968
- attr: "text",
969
- expression: "user.lastName"
970
- },
971
- {
972
- selector: "#remember_me",
973
- attr: "checked",
974
- expression: "user.rememberMe",
975
- twoWay: true
976
- }
977
- ],
978
- data: {
979
- viewModel: {
980
- import: ["java.util.Map"],
981
- variable: [
982
- { name: "map", type: "Map&lt;String, String>" }
983
- ]
984
- }
985
- }
986
- });
406
+ ### Tasks
987
407
 
988
- squared.save();
989
- ```
990
-
991
- Inlining is also supported and might be more convenient for simple layouts. JavaScript is recommended when you are calling `parseDocument` multiple times.
408
+ ```ruby
409
+ Workspace::Series.batch(:ruby, :node, {
410
+ stage: %i[graph test],
411
+ reset: %i[stash pull]
412
+ })
992
413
 
993
- ```
994
- data-viewmodel-{namespace}-{attribute} -> data-viewmodel-android-text
414
+ Workspace::Series.rename("depend", "install")
995
415
  ```
996
416
 
997
- These two additional output parameters are required when using the "**data-viewmodel**" prefix.
417
+ ## Usage
998
418
 
999
- ```html
1000
- <div id="main">
1001
- <label>Name:</label>
1002
- <input id="first_name" type="text" data-viewmodel-android-text="user.firstName" />
1003
- <input id="last_name" type="text" data-viewmodel-android-text="user.lastName" />
1004
- <input id="remember_me" type="checkbox" data-viewmodel-android-checked="=user.rememberMe" /> <!-- "=" for two-way binding -->
1005
- </div>
1006
- ```
419
+ ```sh
420
+ rake -T # List tasks
421
+ rake # rake status (usually "build")
1007
422
 
1008
- ```xml
1009
- <layout>
1010
- <data>
1011
- <import type="java.util.Map" />
1012
- <import type="java.util.List" />
1013
- <variable name="user" type="com.example.User" />
1014
- <variable name="list" type="List&lt;String&gt;" />
1015
- <variable name="map" type="Map&lt;String, String&gt;" />
1016
- <variable name="index" type="int" />
1017
- <variable name="key" type="String" />
1018
- </data>
1019
- <LinearLayout android:id="@+id/main">
1020
- <TextView android:text="Name:" />
1021
- <EditText
1022
- android:id="@+id/first_name"
1023
- android:inputType="text"
1024
- android:text="@{user.firstName}" />
1025
- <EditText
1026
- android:id="@+id/last_name"
1027
- android:inputType="text"
1028
- android:text="@{user.lastName}" />
1029
- <CheckBox
1030
- android:id="@+id/remember_me"
1031
- android:checked="@={user.rememberMe}" />
1032
- </LinearLayout>
1033
- </layout>
1034
- ```
423
+ # GIT_OPTIONS=rebase
424
+ rake pull # All except "default" + "app"
425
+ rake pull:ruby # pathname + optparse + logger
426
+ rake pull:default # pathname + optparse
427
+ rake pull:app # squared
428
+ rake pull:node # emc + pir + squared
1035
429
 
1036
- ### Layout Includes / Merge Tag
430
+ rake build # All except "android"
431
+ rake doc # optparse + android
432
+ rake depend # All except "default"
1037
433
 
1038
- Some applications can benefit from using includes or merge tags to share common templates. Nested includes is supported.
434
+ rake build:ruby # rake compile + rake install + rake install
1039
435
 
1040
- ```html
1041
- <div>
1042
- <div id="item1">Item 1</div>
1043
- <div id="item2" data-android-include-start="true" data-android-include-merge="true" data-pathname-android="app/src/main/res/layout-land" data-filename-android="filename1.xml">Item 2</div>
1044
- <div id="item3">Item 3</div>
1045
- <div id="item4" data-android-include-end="true">Item 4</div>
1046
- <div id="item5" data-android-include="filename2" data-android-include-end="true" data-android-include-viewmodel="exampleData">Item 5</div> <!-- viewModel -->
1047
- </div>
1048
- ```
436
+ rake clean # All except "default" + "app"
437
+ rake clean:ruby # rake clean + rake clean + ["tmp/"]
438
+ rake clean:default # rake clean + rake clean + skip
439
+ rake clean:app # none + skip + ["build/"]
440
+ rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
1049
441
 
1050
- ```javascript
1051
- android.setViewModelByProject({ variable: [{ name: "exampleData", type: "com.example.ExampleData" }] }, "project-1"); // Default is "_"
1052
-
1053
- squared.parseDocument({
1054
- element: document.body,
1055
- projectId: "project-1", // Affects all layouts in same project
1056
- enabledIncludes: true,
1057
- includableElements: [
1058
- {
1059
- selectorStart: "#item2",
1060
- selectorEnd: "#item4",
1061
- pathname: "app/src/main/res/layout-land",
1062
- filename: "filename1.xml",
1063
- merge: true // Multiple elements will auto-merge
1064
- },
1065
- {
1066
- selectorStart: "#item5",
1067
- selectorEnd: "#item5",
1068
- filename: "filename2",
1069
- viewModel: "exampleData" // One element only (merge=false)
1070
- }
1071
- ]
1072
- });
442
+ rake squared:run[#] # List scripts (node)
443
+ rake squared:rake[#] # List tasks (ruby)
1073
444
  ```
1074
- > [!NOTE]
1075
- > By sessionId has precedence when associating a view model.
1076
-
1077
- ```xml
1078
- <LinearLayout>
1079
- <TextView>Item 1</TextView>
1080
- <include layout="@layout/filename1" />
1081
- <include layout="@layout/filename2" app:exampleData="@{exampleData}" />
1082
- </LinearLayout>
1083
- <!-- res/layout/activity_main.xml -->
1084
-
1085
- <merge>
1086
- <TextView>Item 2</TextView>
1087
- <TextView>Item 3</TextView>
1088
- <TextView>Item 4</TextView>
1089
- </merge>
1090
- <!-- res/layout-land/filename1.xml -->
1091
-
1092
- <layout>
1093
- <data>
1094
- <variable name="exampleData" type="com.example.ExampleData" />
1095
- </data>
1096
- <TextView>Item 5</TextView>
1097
- </layout>
1098
- <!-- res/layout/filename2.xml -->
1099
- ```
1100
-
1101
- The attributes "**data-android-include-start**" and "**data-android-include-end**" can only be applied to elements which share the same parent container. See `/demos/gradient.html` for usage instructions.
1102
-
1103
- > [!TIP]
1104
- > "**data-pathname-android**" AND "**data-filename-android**" can also be used with any `parseDocument` base element.
1105
-
1106
- ### Redirecting Output Location
1107
-
1108
- Sometimes it is necessary to extract elements and append them into other containers for it to look identical on the Android device. Redirection will fail if the *target location* is not a block/container element.
1109
445
 
1110
- ```html
1111
- <div>
1112
- <span>Item 1</span>
1113
- <span data-android-target="location">Item 2</span>
1114
- <span data-android-target="location" data-android-target-index="1">Item 3</span>
1115
- <div>
1116
- <ul id="location">
1117
- <li>Item 4</li>
1118
- <li>Item 5</li>
1119
- <!-- span -->
1120
- </ul>
1121
- ```
446
+ ```sh
447
+ rake build:app # squared + cli + sqd-serve
448
+ rake squared:build:workspace # cli + sqd-serve
449
+ rake pull:sqd # sqd-admin
450
+ rake squared:pull:workspace # sqd-serve + sqd-admin
451
+ rake squared:outdated:workspace # cli + sqd-serve + sqd-admin
452
+ ```
453
+
454
+ ## Methods
455
+
456
+ Task:
457
+
458
+ * run
459
+ * script
460
+ * depend
461
+ * archive
462
+ * graph
463
+ * doc
464
+ * lint
465
+ * test
466
+ * clean
467
+
468
+ Non-task:
469
+
470
+ * log
471
+ * exclude
472
+
473
+ ## Styles
474
+
475
+ * banner
476
+ * border
477
+ * header
478
+ * active
479
+ * inline
480
+ * subject
481
+ * border
482
+ * warn
483
+ * caution
484
+ * current
485
+ * latest
486
+ * extra
487
+ * major
488
+ * red
489
+ * yellow
490
+ * green
491
+
492
+ ## Git
493
+
494
+ Most project classes will inherit from `Git` which enables these tasks:
495
+
496
+ | Task | Git | Command |
497
+ | :--------- | :--------------- | :-------------------------------------------- |
498
+ | branch | branch | create set delete move copy list edit current |
499
+ | checkout | checkout | commit branch track detach path |
500
+ | commit | commit | add all amend amend-orig fixup |
501
+ | diff | diff | head cached branch files between contain |
502
+ | fetch | fetch | origin remote all |
503
+ | files | ls-files | cached modified deleted others |
504
+ | git | | add blame clean mv rm revert status |
505
+ | merge | merge | commit no-commit send |
506
+ | pull | pull | origin remote all |
507
+ | rebase | rebase | branch onto send |
508
+ | refs | ls-remote --refs | heads tags remote |
509
+ | reset | reset | commit index patch mode |
510
+ | restore | restore | staged worktree |
511
+ | rev | rev | commit output |
512
+ | show | show | format oneline |
513
+ | stash | stash | push pop apply branch drop clear list all |
514
+ | submodule | submodule | status update branch url sync |
515
+ | switch | switch | branch create detach |
516
+ | tag | tag | add sign delete list |
517
+
518
+ You can disable all of them at once using the `exclude` property.
519
+
520
+ ```ruby
521
+ Workspace::Application.exclude('autostash', 'rebase')
522
+
523
+ Workspace::Application
524
+ .new
525
+ .add("squared", exclude: :git)
526
+ ```
527
+
528
+ You can disable one or more of them using the `pass` property as a *string*.
529
+
530
+ ```ruby
531
+ Workspace::Application
532
+ .new
533
+ .add("squared", pass: ["pull"], ref: :node)
534
+ .pass("pull", ref: :node) { read_packagemanager(:private) }
535
+ ```
536
+
537
+ ### Commit Hash
538
+
539
+ Commands which use commit hashes are parsed using a ":" prefix as to not be confused for an option.
1122
540
 
1123
- ```xml
1124
- <LinearLayout>
1125
- <TextView>Item 1</TextView>
1126
- </LinearLayout>
1127
- <LinearLayout>
1128
- <TextView>Item 4</TextView>
1129
- <TextView>Item 3</TextView>
1130
- <TextView>Item 5</TextView>
1131
- <TextView>Item 2</TextView>
1132
- </LinearLayout>
541
+ ```sh
542
+ rake squared:log:view[:af012345] # git log af012345
543
+ rake squared:log:view[#{af012345}] # deprecated
544
+ rake squared:log:view[H1,HEAD^5,all,lib,./H12345] # git log --all @~1 @^5 -- 'lib' 'H12345'
1133
545
  ```
1134
546
 
1135
- Using `target` into a ConstraintLayout or RelativeLayout container will not include automatic positioning.
1136
-
1137
- ### Custom Attributes
1138
-
1139
- System or extension generated attributes can be overridden preceding finalization. They will only be visible on the declared framework.
1140
-
1141
- ```
1142
- data-android-attr-{namespace}? -> Default is "android"
1143
- ```
547
+ ## Environment
1144
548
 
1145
- ```html
1146
- <div id="customId"
1147
- data-android-attr="layout_width::match_parent;layout_height::match_parent"
1148
- data-android-attr-app="layout_scrollFlags::scroll|exitUntilCollapsed">
1149
- </div>
1150
- ```
549
+ ### Path
1151
550
 
1152
- ```xml
1153
- <LinearLayout
1154
- android:id="@+id/customId"
1155
- android:layout_width="match_parent"
1156
- android:layout_height="match_parent"
1157
- app:layout_scrollFlags="scroll|exitUntilCollapsed" />
1158
- ```
551
+ All project binary programs can have their executable path set to a non-global alias.
1159
552
 
1160
- ```javascript
1161
- const node = squared.findDocumentNode("customId"); // querySelector is supported
1162
- node.android("layout_width", "match_parent");
1163
- node.android("layout_height", "match_parent");
1164
- node.app("layout_scrollFlags", "scroll|exitUntilCollapsed");
553
+ ```ruby
554
+ Common::PATH.update({
555
+ GIT: "/usr/bin/git", # PATH_GIT=/usr/bin/git
556
+ TAR: "/opt/archivers/tar", # PATH_TAR=/opt/archivers/tar
557
+ UNZIP: "/opt/archivers/unzip",
558
+ GEM: "~/.rvm/gems/ruby-3.4.0/bin/gem",
559
+ BUNDLE: "~/.rvm/gems/ruby-3.4.0/bin/bundle",
560
+ RAKE: "~/.rvm/gems/ruby-3.4.0/bin/rake",
561
+ NPM: "/opt/node/v22.0.0/bin/npm",
562
+ PYTHON: "#{ENV["PYTHONPATH"]}/bin/python"
563
+ })
1165
564
  ```
1166
565
 
1167
- ### SVG animations
566
+ ### Build
1168
567
 
1169
- Only the XML based layout and resource files can be viewed on the Android device/emulator without any Java/Kotlin backend code. To play animations in the emulator you also have to `start` the animation in *MainActivity.java*.
1170
-
1171
- ```java
1172
- import android.graphics.drawable.Animatable;
1173
-
1174
- android.widget.ImageView imageView1 = findViewById(R.id.imageview_1);
1175
- if (imageView1 != null) {
1176
- Animatable animatable = (Animatable) imageView1.getDrawable();
1177
- animatable.start();
1178
- }
568
+ ```ruby
569
+ Workspace::Application
570
+ .new
571
+ .add("squared", run: "gcc a.c -o a.o", opts: { __debug__: { g: true, O2: true, c: nil }, c: true, j: 4 }) # gcc a.c -o a.o -c -j4
1179
572
  ```
1180
573
 
1181
- ### Jetpack Compose
574
+ ```sh
575
+ BUILD_TYPE # global
576
+ ${PROG}_COLOR=0 # --no-color (e.g. GIT_COLOR)
1182
577
 
1183
- Most mobile applications do not have a deeply nested hierarchy and are generally better to implement using declarative programming.
578
+ # :env :run :args :opts :type
579
+ # LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -g -O2
580
+ BUILD_${NAME} # gcc a.c -o a.o
581
+ BUILD_${NAME}_OPTS # -g
582
+ BUILD_${NAME}_ENV # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
583
+ BUILD_${NAME}_TYPE # debug
1184
584
 
1185
- ```javascript
1186
- squared.settings.composableElements = ["main", "#content", "--boxShadow", "--height=300px", {
1187
- selector: "main",
1188
- android: {
1189
- layout_height: "match_parent"
1190
- },
1191
- tools: {
1192
- composableName: "com.example.compose.Preview"
1193
- }
1194
- }];
1195
- squared.settings.createBuildDependencies = true; // Optional
1196
- ```
585
+ # :env :script :opts :args
586
+ # NODE_ENV="production" NO_COLOR="1" npm run build:dev --loglevel=error --workspaces=false -- --quiet
587
+ BUILD_${NAME} # build:dev
588
+ BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
589
+ BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
590
+ BUILD_${NAME}_DEV # pattern,0,1 (:dev)
591
+ BUILD_${NAME}_PROD # pattern,0,1 (:prod)
592
+ ${REF}_${NAME}_OPTS # --quiet (e.g. NODE_SQUARED_OPTS)
1197
593
 
1198
- You can also do it using the "**android.substitute**" extension directly inside the HTML element.
594
+ BUILD_${NAME}=0 # skip project
595
+ BUILD_${NAME}_VERSION=0.1.0 # publish + detection
1199
596
 
1200
- ```javascript
1201
- // android.substitute is only used here to demonstrate using extensions
597
+ BANNER=0 # hide banner
598
+ BANNER_${NAME}=0 #
1202
599
 
1203
- squared.add(["android.substitute", {
1204
- element: {
1205
- content: { android: { layout_width: "match_parent" } }
1206
- }
1207
- }]);
1208
-
1209
- const items = squared.attr("android.substitute", "viewAttributes");
1210
- items.push("hint", "buttonTint");
1211
- /* OR */
1212
- squared.attr("android.substitute", "viewAttributes", items.concat(["hint", "buttonTint"])); // Attributes to preserve (default is "android.view.View")
1213
- squared.attr("android.substitute", "attributeMapping", { "android:src": "app:srcCompat", "icon": "navigationIcon" /* android */});
1214
-
1215
- squared.parseDocument({
1216
- element: document.body,
1217
- substitutableElements: [{
1218
- selector: "#content",
1219
- tag: "androidx.compose.ui.platform.ComposeView",
1220
- renderChildren: false
1221
- }],
1222
-
1223
- // Some extensions have convenience properties
1224
- enabledSubstitute: true,
1225
- /* OR */
1226
- include: ["android.substitute"]
1227
- });
1228
- ```
600
+ REVBUILD_FORCE=1 # Rebuild all targets
601
+ REVBUILD_FORCE_${NAME}=1 # Rebuild project
1229
602
 
1230
- ```html
1231
- <body>
1232
- <header style="height: 100px"></header>
1233
- <main id="content"
1234
- data-use="android.substitute"
1235
- data-android-substitute-tag="androidx.compose.ui.platform.ComposeView"
1236
- style="height: 300px; box-shadow: 10px 5px 5px black;">
1237
- <!-- Interior elements are not rendered -->
1238
- </main>
1239
- <footer style="height: 80px"></footer>
1240
- </body>
603
+ PREREQS_${NAME}=build,copy # Class method name to invoke
604
+ PREREQS_${REF}=depend # e.g. Node
1241
605
  ```
1242
606
 
1243
- Compose will remove child elements by default. You can preserve them by explictly using the renderChildren property. (data-android-substitute-render-children="true")
607
+ ### Graph
1244
608
 
1245
- ```html
1246
- <div id="fragment"
1247
- data-use="android.substitute"
1248
- data-android-substitute-tag="androidx.fragment.app.FragmentContainerView"
1249
- data-android-substitute-render-children="false"
1250
- data-android-attr="name::com.github.fragment;tag::example">
1251
- <!-- Interior elements are not rendered -->
1252
- </div>
609
+ ```sh
610
+ GRAPH_${NAME} # depend,build => squared:depend + squared:build
611
+ GRAPH_${NAME}_PASS # -emc,pir,express => pir + express
1253
612
  ```
1254
613
 
1255
- You can also use "**android.substitute**" to create fragments within a layout similar to Compose.
1256
-
1257
- Usually you do not render child elements when using Compose View. There are some cases where it can be used effectively to reproduce the desired layout.
1258
-
1259
- ```javascript
1260
- squared.parseDocument({
1261
- element: document.body,
1262
- include: ["android.substitute"], // OR: settings.enabledSubstitute
1263
- substitutableElements: [{
1264
- selector: "#navigation",
1265
- tag: "com.google.android.material.tabs.TabLayout",
1266
- tagChild: "com.google.android.material.tabs.TabItem",
1267
- tagChildAttr: {
1268
- android: {
1269
- layout_height: "match_parent"
1270
- }
1271
- },
1272
- renderChildren: true,
1273
- autoLayout: true
1274
- }]
1275
- });
1276
- ```
614
+ ### Logger
1277
615
 
1278
- ```html
1279
- <ul id="navigation"
1280
- data-use-android="android.substitute"
1281
- data-android-attr="layout_height::match_parent"
1282
- data-android-substitute-tag="com.google.android.material.tabs.TabLayout"
1283
- data-android-substitute-tag-child="com.google.android.material.tabs.TabItem"
1284
- data-android-substitute-tag-child-attr="layout_height::match_parent"
1285
- data-android-substitute-auto-layout="true">
1286
- <li>TAB 1</li>
1287
- <li>TAB 2</li>
1288
- <li>TAB 3</li>
1289
- </ul>
1290
- ```
616
+ These global options also can target the project suffix `${NAME}`. (e.g. LOG_FILE_EMC)
1291
617
 
1292
- ```xml
1293
- <com.google.android.material.tabs.TabLayout
1294
- android:id="@+id/navigation"
1295
- android:layout_height="match_parent"
1296
- android:layout_width="wrap_content">
1297
- <com.google.android.material.tabs.TabItem
1298
- android:layout_height="match_parent"
1299
- android:layout_width="wrap_content"
1300
- android:text="@string/tab_1" />
1301
- <com.google.android.material.tabs.TabItem
1302
- android:layout_height="match_parent"
1303
- android:layout_width="wrap_content"
1304
- android:text="@string/tab_2" />
1305
- <com.google.android.material.tabs.TabItem
1306
- android:layout_height="match_parent"
1307
- android:layout_width="wrap_content"
1308
- android:text="@string/tab_3" />
1309
- </com.google.android.material.tabs.TabLayout>
618
+ ```sh
619
+ LOG_FILE # %Y-%m-%d.log
620
+ # OR
621
+ LOG_AUTO # year,y,month,m,day,d,1
622
+ # Optional
623
+ LOG_DIR # exist?
624
+ LOG_LEVEL # See gem "logger"
1310
625
  ```
1311
626
 
1312
- ### Downloadable Fonts
627
+ ### Git
1313
628
 
1314
- Google Fonts are pre-installed and can be used without any additional configuration.
629
+ * Version: 2.50
1315
630
 
1316
- * [Guide](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts)
1317
-
1318
- ```xml
1319
- <!-- build.gradle -->
1320
- dependencies {
1321
- implementation 'androidx.appcompat:appcompat:1.6.0' <!-- createBuildDependencies = true -->
1322
- <!-- OR -->
1323
- implementation 'com.android.support:appcompat-v7:28.0.0'
1324
- }
1325
-
1326
- <!-- AndroidManifest.xml -->
1327
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- createManifest = true -->
1328
- <application android:theme="@style/AppTheme">
1329
- <meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" />
1330
- </application>
1331
- </manifest>
1332
- ```
1333
-
1334
- ```javascript
1335
- // https://developers.google.com/fonts/docs/developer_api
1336
-
1337
- await android.addFontProvider(
1338
- "com.google.android.gms.fonts",
1339
- "com.google.android.gms",
1340
- ["MIIEqDCCA5CgAwIBAgIJANWFuGx9007...", "MIIEQzCCAyugAwIBAgIJAMLgh0Zk..."],
1341
- "https://www.googleapis.com/webfonts/v1/webfonts?key=1234567890" // JSON object is synchronous
1342
- );
1343
- /* OR */
1344
- squared.attr("android.resource.fonts", "installGoogleFonts", false); // Use browser and local fonts only
1345
- ```
631
+ ```sh
632
+ GIT_OPTIONS=q,strategy=ort # all
633
+ GIT_OPTIONS_${NAME}=v,ff # project only
634
+ GIT_AUTOSTASH=1 # rebase (all)
635
+ GIT_AUTOSTASH_${NAME}=0 # rebase (project only)
636
+ ```
637
+
638
+ | Command | Flag | ENV |
639
+ | :--------- | :---------------- | :-------------------------------------------------------------------- |
640
+ | branch | create | TRACK=0,1,s F|FORCE |
641
+ | branch | move copy | F|FORCE |
642
+ | branch | set delete | COUNT=n |
643
+ | branch | global | SYNC |
644
+ | checkout | branch | DETACH TRACK=s COUNT=n |
645
+ | checkout | detach | REFLOG=1 |
646
+ | checkout | track | COUNT=n |
647
+ | checkout | global path | HEAD=s PATHSPEC=s |
648
+ | checkout | * | F|FORCE MERGE |
649
+ | clone | * | DEPTH=n ORIGIN=s BRANCH=s REVISION=s LOCAL=0,1 |
650
+ | commit | * | UPSTREAM=s DRY_RUN EDIT=0 M|MESSAGE=s |
651
+ | diff | -between -contain | MERGE_BASE |
652
+ | diff | head branch | INDEX=n |
653
+ | diff | * | PATHSPEC=s |
654
+ | fetch | -remote | ALL |
655
+ | fetch | remote | REFSPEC=s |
656
+ | fetch | * | F|FORCE RECURSE_SUBMODULES=0,1,s |
657
+ | git | rm | PATHSPEC=s |
658
+ | log | * | PATHSPEC=s |
659
+ | pull | remote | REFSPEC=s |
660
+ | pull | -remote | REBASE=0,1 ALL |
661
+ | pull | all | FF_ONLY=0 |
662
+ | pull | * | AUTOSTASH F|FORCE RECURSE_SUBMODULES=0,1,s |
663
+ | rebase | branch | HEAD=s |
664
+ | rebase | onto | INTERACTIVE I HEAD=s |
665
+ | reset | mode (mixed) | N REFRESH=0 |
666
+ | reset | index | PATHSPEC=s |
667
+ | reset | commit | COUNT=n REFLOG=1 |
668
+ | reset | -commit | HEAD=s |
669
+ | restore | * | PATHSPEC=s |
670
+ | revbuild | global | UNTRACKED_FILES=s IGNORE_SUBMODULES=s IGNORED=s (status) |
671
+ | stash | push | PATHSPEC=s |
672
+ | stash | global | ALL=0,1 KEEP_INDEX=0,1 INCLUDE_UNTRACKED=0,1 STAGED=0,1 M|MESSAGE=s |
673
+ | status | global | BRANCH LONG IGNORE_SUBMODULES=s,0-3 PATHSPEC=s |
674
+ | submodule | -branch -url | R|RECURSIVE |
675
+ | switch | detach | REFLOG=1 |
676
+ | switch | -detach | HEAD=s |
677
+ | switch | * | F|FORCE |
678
+ | tag | add | SIGN FORCE HEAD=s M|MESSAGE=s |
679
+ | tag | sign | F|FORCE HEAD=s M|MESSAGE=s |
680
+ | tag | delete | COUNT=n |
681
+ | rev | commit branch | HEAD=s |
682
+
683
+ ### Docker
684
+
685
+ * Version: 28.3
1346
686
 
1347
- ### Excluding Procedures / Applied Attributes
687
+ ```sh
688
+ DOCKER_OPTIONS=q,no-cache # all
689
+ DOCKER_OPTIONS_${NAME}=v,no-cache=false # project only (override)
690
+ DOCKER_TAG=latest # all
691
+ DOCKER_TAG_${NAME}=v0.1.0 # project only (override)
692
+ DOCKER_ALL=1 # list every image/container
693
+ DOCKER_Y=1 # confirm all
694
+ ```
695
+
696
+ | Command | Flag | ENV |
697
+ | :--------- | :---------------- | :---------------------------------------------- |
698
+ | buildx | build | TAG=s |
699
+ | buildx | bake | SERVICE=s |
700
+ | compose | build | TARGET=s |
701
+ | container | commit | REGISTRY=s PLATFORM=s DISABLE_CONTENT_TRUST=0,1 |
702
+ | image | rm | Y=0,1 |
703
+ | image | push | TAG=s REGISTRY=s |
1348
704
 
1349
- Most attributes can be excluded from the generated XML using the dataset feature in HTML. One or more can be applied to any tag using the OR "**|**" operator. These may cause warnings when you compile your project and should only be used when an extension has their custom attributes overwritten.
705
+ ### Repo
1350
706
 
1351
- NOTE: Defining an element "**id**" will prevent it from being removed during the optimization phase.
707
+ These global options also can target the application main suffix `${NAME}`. (e.g. REPO_ROOT_SQUARED)
1352
708
 
1353
- ```html
1354
- <div data-exclude-section="DOM_TRAVERSE | EXTENSION | RENDER | ALL"
1355
- data-exclude-procedure="CONSTRAINT | LAYOUT | ALIGNMENT | ACCESSIBILITY | LOCALIZATION | CUSTOMIZATION | OPTIMIZATION | ALL"
1356
- data-exclude-resource="BOX_STYLE | BOX_SPACING | FONT_STYLE | VALUE_STRING | IMAGE_SOURCE | ASSET | ALL"
1357
- data-exclude-optimization="EXCLUDE | INHERIT | ALIGNMENT | POSITION | DIMENSION | MARGIN | PADDING | BASELINE | WHITESPACE | TRANSLATE | TRANSFORM | SCALING">
1358
- </div>
1359
- <div>
1360
- <span data-exclude-resource="FONT_STYLE">content</span>
1361
- <input id="cb1" type="checkbox" data-exclude-procedure="ACCESSIBILITY"><label for="cb1">checkbox text</label>
1362
- </div>
709
+ ```sh
710
+ REPO_ROOT # parent dir
711
+ REPO_HOME # project dir (main)
712
+ REPO_BUILD # run,script
713
+ REPO_GROUP # string
714
+ REPO_REF # e.g. ruby,node
715
+ REPO_DEV # pattern,0,1
716
+ REPO_PROD # pattern,0,1
717
+ REPO_WARN # 0,1
718
+ REPO_SYNC # 0,1
719
+ REPO_URL # manifest repository
720
+ REPO_MANIFEST # e.g. latest,nightly,prod
721
+ REPO_GROUPS # e.g. base,prod,docs
722
+ REPO_STAGE # 0,1,2,3
723
+ REPO_SUBMODULLES # 0,1
724
+ REPO_TIMEOUT # confirm dialog (seconds)
1363
725
  ```
1364
726
 
1365
727
  ## LICENSE
1366
728
 
1367
- BSD 3-Clause
729
+ BSD 3-Clause