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