squared 0.5.4 → 0.5.5

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