@things-factory/shell 10.1.254 → 10.1.259
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/themes/domain-theme-cherry-red.css +1 -8
- package/client/themes/domain-theme-forest-green.css +1 -8
- package/client/themes/domain-theme-ocean-blue.css +1 -8
- package/client/themes/domain-theme-royal-purple.css +1 -8
- package/client/themes/domain-theme-sunset-orange.css +1 -8
- package/package.json +2 -2
- package/tests/destructive-theme-colors.test.mjs +28 -0
- package/translations/en.json +7 -1
- package/translations/es.json +13 -1
- package/translations/ja.json +13 -1
- package/translations/ko.json +7 -1
- package/translations/ms.json +13 -1
- package/translations/zh.json +13 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Destructive colors inherit the shared light/dark semantic palette, independently of the brand. */
|
|
1
2
|
.domain-theme-cherry-red {
|
|
2
3
|
--md-sys-color-primary: rgb(211 47 47);
|
|
3
4
|
--md-sys-color-surface-tint: rgb(211 47 47);
|
|
@@ -12,10 +13,6 @@
|
|
|
12
13
|
--md-sys-color-on-tertiary: rgb(255 255 255);
|
|
13
14
|
--md-sys-color-tertiary-container: rgb(252 228 236);
|
|
14
15
|
--md-sys-color-on-tertiary-container: rgb(136 14 79);
|
|
15
|
-
--md-sys-color-error: #fe594e;
|
|
16
|
-
--md-sys-color-on-error: rgb(255 255 255);
|
|
17
|
-
--md-sys-color-error-container: rgb(255 218 215);
|
|
18
|
-
--md-sys-color-on-error-container: #fe594e;
|
|
19
16
|
--md-sys-color-background: rgb(255 250 250);
|
|
20
17
|
--md-sys-color-on-background: rgb(35 25 25);
|
|
21
18
|
--md-sys-color-surface: rgb(255 250 250);
|
|
@@ -64,10 +61,6 @@
|
|
|
64
61
|
--md-sys-color-on-tertiary: rgb(136 14 79);
|
|
65
62
|
--md-sys-color-tertiary-container: rgb(173 20 87);
|
|
66
63
|
--md-sys-color-on-tertiary-container: rgb(252 228 236);
|
|
67
|
-
--md-sys-color-error: #fbafa9;
|
|
68
|
-
--md-sys-color-on-error: #fe594e;
|
|
69
|
-
--md-sys-color-error-container: #fe594e;
|
|
70
|
-
--md-sys-color-on-error-container: #fbafa9;
|
|
71
64
|
--md-sys-color-background: rgb(25 17 17);
|
|
72
65
|
--md-sys-color-on-background: rgb(238 226 226);
|
|
73
66
|
--md-sys-color-surface: rgb(25 17 17);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Destructive colors inherit the shared light/dark semantic palette, independently of the brand. */
|
|
1
2
|
.domain-theme-forest-green {
|
|
2
3
|
--md-sys-color-primary: rgb(56 142 60);
|
|
3
4
|
--md-sys-color-surface-tint: rgb(56 142 60);
|
|
@@ -12,10 +13,6 @@
|
|
|
12
13
|
--md-sys-color-on-tertiary: rgb(255 255 255);
|
|
13
14
|
--md-sys-color-tertiary-container: rgb(178 223 219);
|
|
14
15
|
--md-sys-color-on-tertiary-container: rgb(0 77 64);
|
|
15
|
-
--md-sys-color-error: #fe594e;
|
|
16
|
-
--md-sys-color-on-error: rgb(255 255 255);
|
|
17
|
-
--md-sys-color-error-container: rgb(255 218 215);
|
|
18
|
-
--md-sys-color-on-error-container: #fe594e;
|
|
19
16
|
--md-sys-color-background: rgb(250 253 250);
|
|
20
17
|
--md-sys-color-on-background: rgb(25 32 25);
|
|
21
18
|
--md-sys-color-surface: rgb(250 253 250);
|
|
@@ -64,10 +61,6 @@
|
|
|
64
61
|
--md-sys-color-on-tertiary: rgb(0 77 64);
|
|
65
62
|
--md-sys-color-tertiary-container: rgb(0 105 92);
|
|
66
63
|
--md-sys-color-on-tertiary-container: rgb(178 223 219);
|
|
67
|
-
--md-sys-color-error: #fbafa9;
|
|
68
|
-
--md-sys-color-on-error: #fe594e;
|
|
69
|
-
--md-sys-color-error-container: #fe594e;
|
|
70
|
-
--md-sys-color-on-error-container: #fbafa9;
|
|
71
64
|
--md-sys-color-background: rgb(17 22 17);
|
|
72
65
|
--md-sys-color-on-background: rgb(226 235 226);
|
|
73
66
|
--md-sys-color-surface: rgb(17 22 17);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Destructive colors inherit the shared light/dark semantic palette, independently of the brand. */
|
|
1
2
|
.domain-theme-ocean-blue {
|
|
2
3
|
--md-sys-color-primary: rgb(30 136 229);
|
|
3
4
|
--md-sys-color-surface-tint: rgb(30 136 229);
|
|
@@ -12,10 +13,6 @@
|
|
|
12
13
|
--md-sys-color-on-tertiary: rgb(255 255 255);
|
|
13
14
|
--md-sys-color-tertiary-container: rgb(178 235 242);
|
|
14
15
|
--md-sys-color-on-tertiary-container: rgb(0 96 100);
|
|
15
|
-
--md-sys-color-error: #fe594e;
|
|
16
|
-
--md-sys-color-on-error: rgb(255 255 255);
|
|
17
|
-
--md-sys-color-error-container: rgb(255 218 215);
|
|
18
|
-
--md-sys-color-on-error-container: #fe594e;
|
|
19
16
|
--md-sys-color-background: rgb(250 252 255);
|
|
20
17
|
--md-sys-color-on-background: rgb(23 29 35);
|
|
21
18
|
--md-sys-color-surface: rgb(250 252 255);
|
|
@@ -64,10 +61,6 @@
|
|
|
64
61
|
--md-sys-color-on-tertiary: rgb(0 96 100);
|
|
65
62
|
--md-sys-color-tertiary-container: rgb(0 131 143);
|
|
66
63
|
--md-sys-color-on-tertiary-container: rgb(178 235 242);
|
|
67
|
-
--md-sys-color-error: #fbafa9;
|
|
68
|
-
--md-sys-color-on-error: #fe594e;
|
|
69
|
-
--md-sys-color-error-container: #fe594e;
|
|
70
|
-
--md-sys-color-on-error-container: #fbafa9;
|
|
71
64
|
--md-sys-color-background: rgb(16 22 28);
|
|
72
65
|
--md-sys-color-on-background: rgb(226 234 242);
|
|
73
66
|
--md-sys-color-surface: rgb(16 22 28);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Destructive colors inherit the shared light/dark semantic palette, independently of the brand. */
|
|
1
2
|
.domain-theme-royal-purple {
|
|
2
3
|
--md-sys-color-primary: rgb(123 31 162);
|
|
3
4
|
--md-sys-color-surface-tint: rgb(123 31 162);
|
|
@@ -12,10 +13,6 @@
|
|
|
12
13
|
--md-sys-color-on-tertiary: rgb(255 255 255);
|
|
13
14
|
--md-sys-color-tertiary-container: rgb(209 196 233);
|
|
14
15
|
--md-sys-color-on-tertiary-container: rgb(49 27 146);
|
|
15
|
-
--md-sys-color-error: #fe594e;
|
|
16
|
-
--md-sys-color-on-error: rgb(255 255 255);
|
|
17
|
-
--md-sys-color-error-container: rgb(255 218 215);
|
|
18
|
-
--md-sys-color-on-error-container: #fe594e;
|
|
19
16
|
--md-sys-color-background: rgb(253 250 255);
|
|
20
17
|
--md-sys-color-on-background: rgb(30 25 35);
|
|
21
18
|
--md-sys-color-surface: rgb(253 250 255);
|
|
@@ -64,10 +61,6 @@
|
|
|
64
61
|
--md-sys-color-on-tertiary: rgb(49 27 146);
|
|
65
62
|
--md-sys-color-tertiary-container: rgb(69 39 160);
|
|
66
63
|
--md-sys-color-on-tertiary-container: rgb(209 196 233);
|
|
67
|
-
--md-sys-color-error: #fbafa9;
|
|
68
|
-
--md-sys-color-on-error: #fe594e;
|
|
69
|
-
--md-sys-color-error-container: #fe594e;
|
|
70
|
-
--md-sys-color-on-error-container: #fbafa9;
|
|
71
64
|
--md-sys-color-background: rgb(20 16 25);
|
|
72
65
|
--md-sys-color-on-background: rgb(230 226 236);
|
|
73
66
|
--md-sys-color-surface: rgb(20 16 25);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* Destructive colors inherit the shared light/dark semantic palette, independently of the brand. */
|
|
1
2
|
.domain-theme-sunset-orange {
|
|
2
3
|
--md-sys-color-primary: rgb(245 124 0);
|
|
3
4
|
--md-sys-color-surface-tint: rgb(245 124 0);
|
|
@@ -12,10 +13,6 @@
|
|
|
12
13
|
--md-sys-color-on-tertiary: rgb(0 0 0);
|
|
13
14
|
--md-sys-color-tertiary-container: rgb(255 249 196);
|
|
14
15
|
--md-sys-color-on-tertiary-container: rgb(255 143 0);
|
|
15
|
-
--md-sys-color-error: #fe594e;
|
|
16
|
-
--md-sys-color-on-error: rgb(255 255 255);
|
|
17
|
-
--md-sys-color-error-container: rgb(255 218 215);
|
|
18
|
-
--md-sys-color-on-error-container: #fe594e;
|
|
19
16
|
--md-sys-color-background: rgb(255 252 248);
|
|
20
17
|
--md-sys-color-on-background: rgb(35 30 24);
|
|
21
18
|
--md-sys-color-surface: rgb(255 252 248);
|
|
@@ -64,10 +61,6 @@
|
|
|
64
61
|
--md-sys-color-on-tertiary: rgb(255 143 0);
|
|
65
62
|
--md-sys-color-tertiary-container: rgb(255 160 0);
|
|
66
63
|
--md-sys-color-on-tertiary-container: rgb(255 249 196);
|
|
67
|
-
--md-sys-color-error: #fbafa9;
|
|
68
|
-
--md-sys-color-on-error: #fe594e;
|
|
69
|
-
--md-sys-color-error-container: #fe594e;
|
|
70
|
-
--md-sys-color-on-error-container: #fbafa9;
|
|
71
64
|
--md-sys-color-background: rgb(24 20 14);
|
|
72
65
|
--md-sys-color-on-background: rgb(234 228 220);
|
|
73
66
|
--md-sys-color-surface: rgb(24 20 14);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@things-factory/shell",
|
|
3
|
-
"version": "10.1.
|
|
3
|
+
"version": "10.1.259",
|
|
4
4
|
"description": "Core module for framework",
|
|
5
5
|
"bin": {
|
|
6
6
|
"things-factory": "bin/things-factory",
|
|
@@ -142,5 +142,5 @@
|
|
|
142
142
|
"pg": "^8.7.3",
|
|
143
143
|
"sqlite3": "^5.0.8"
|
|
144
144
|
},
|
|
145
|
-
"gitHead": "
|
|
145
|
+
"gitHead": "e7283779ee700b4b124226f0ad22d7c1e4063ae7"
|
|
146
146
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { test } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { readFileSync, readdirSync } from 'node:fs'
|
|
4
|
+
|
|
5
|
+
const directory = new URL('../client/themes/', import.meta.url)
|
|
6
|
+
const declarations = text => Object.fromEntries([...text.matchAll(/(--md-sys-color-(?:on-)?error(?:-container)?):\s*([^;]+);/g)].map(match => [match[1], match[2].trim()]))
|
|
7
|
+
function luminance(color) {
|
|
8
|
+
const channels = color.startsWith('#')
|
|
9
|
+
? color.slice(1).match(/../g).map(part => parseInt(part, 16))
|
|
10
|
+
: color.match(/[\d.]+/g).slice(0, 3).map(Number)
|
|
11
|
+
return channels.map(n => n / 255).map(n => n <= .04045 ? n / 12.92 : ((n + .055) / 1.055) ** 2.4).reduce((sum, n, i) => sum + n * [.2126, .7152, .0722][i], 0)
|
|
12
|
+
}
|
|
13
|
+
for (const mode of ['light', 'dark']) {
|
|
14
|
+
test(`${mode}: both destructive foreground/background pairs retain readable contrast`, () => {
|
|
15
|
+
const colors = declarations(readFileSync(new URL(`${mode}.css`, directory), 'utf8'))
|
|
16
|
+
for (const suffix of ['', '-container']) {
|
|
17
|
+
const a = luminance(colors[`--md-sys-color-error${suffix}`])
|
|
18
|
+
const b = luminance(colors[`--md-sys-color-on-error${suffix}`])
|
|
19
|
+
const contrast = (Math.max(a, b) + .05) / (Math.min(a, b) + .05)
|
|
20
|
+
assert.ok(contrast >= 4.5, `${mode}${suffix}: ${contrast.toFixed(2)}`)
|
|
21
|
+
}
|
|
22
|
+
})
|
|
23
|
+
}
|
|
24
|
+
for (const file of readdirSync(directory).filter(name => /^domain-theme-.*\.css$/.test(name))) {
|
|
25
|
+
test(`${file}: branding inherits destructive colors from the shared light/dark palette`, () => {
|
|
26
|
+
assert.deepEqual(declarations(readFileSync(new URL(file, directory), 'utf8')), {})
|
|
27
|
+
})
|
|
28
|
+
}
|
package/translations/en.json
CHANGED
|
@@ -212,5 +212,11 @@
|
|
|
212
212
|
"error.ip-protectedlist-entry-invalid": "'{entry}' in the protected list is not an IP address regular expression. For example: ^10\\.0\\.",
|
|
213
213
|
"error.ip-whitelist-entry-empty": "The allow list has an empty entry. Leave nothing empty between commas.",
|
|
214
214
|
"error.ip-blacklist-entry-empty": "The block list has an empty entry. Leave nothing empty between commas.",
|
|
215
|
-
"error.ip-protectedlist-entry-empty": "The protected list has an empty entry. Leave nothing empty between commas."
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "The protected list has an empty entry. Leave nothing empty between commas.",
|
|
216
|
+
"field.control-no": "control No.",
|
|
217
|
+
"field.note": "note",
|
|
218
|
+
"figure.label.data-bindings": "bindings",
|
|
219
|
+
"text.delete": "delete",
|
|
220
|
+
"text.copy": "copy",
|
|
221
|
+
"text.scenario": "scenario"
|
|
216
222
|
}
|
package/translations/es.json
CHANGED
|
@@ -206,5 +206,17 @@
|
|
|
206
206
|
"text.tax-kind-zero-rated": "Tipo cero",
|
|
207
207
|
"text.tax-kind-exempt": "Exento",
|
|
208
208
|
"button.rename": "cambiar nombre",
|
|
209
|
-
"button.search": "buscar"
|
|
209
|
+
"button.search": "buscar",
|
|
210
|
+
"error.ip-whitelist-entry-invalid": "'{entry}' en la lista de permitidos no es una expresión regular de dirección IP. Por ejemplo: ^10\\.0\\.",
|
|
211
|
+
"error.ip-blacklist-entry-invalid": "'{entry}' en la lista de bloqueo no es una expresión regular de dirección IP. Por ejemplo: ^10\\.0\\.",
|
|
212
|
+
"error.ip-protectedlist-entry-invalid": "'{entry}' en la lista protegida no es una expresión regular de dirección IP. Por ejemplo: ^10\\.0\\.",
|
|
213
|
+
"error.ip-whitelist-entry-empty": "La lista de permitidos tiene una entrada vacía. No deje nada vacío entre comas.",
|
|
214
|
+
"error.ip-blacklist-entry-empty": "La lista de bloqueo tiene una entrada vacía. No deje nada vacío entre comas.",
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "La lista protegida tiene una entrada vacía. No deje nada vacío entre comas.",
|
|
216
|
+
"field.control-no": "n.º de control",
|
|
217
|
+
"field.note": "nota",
|
|
218
|
+
"figure.label.data-bindings": "vínculos",
|
|
219
|
+
"text.delete": "eliminar",
|
|
220
|
+
"text.copy": "copiar",
|
|
221
|
+
"text.scenario": "escenario"
|
|
210
222
|
}
|
package/translations/ja.json
CHANGED
|
@@ -206,5 +206,17 @@
|
|
|
206
206
|
"text.tax-kind-zero-rated": "ゼロ税率",
|
|
207
207
|
"text.tax-kind-exempt": "免税",
|
|
208
208
|
"button.rename": "名前変更",
|
|
209
|
-
"button.search": "検索"
|
|
209
|
+
"button.search": "検索",
|
|
210
|
+
"error.ip-whitelist-entry-invalid": "許可リストの '{entry}' はIPアドレスの正規表現ではありません。例: ^10\\.0\\.",
|
|
211
|
+
"error.ip-blacklist-entry-invalid": "ブロックリストの '{entry}' はIPアドレスの正規表現ではありません。例: ^10\\.0\\.",
|
|
212
|
+
"error.ip-protectedlist-entry-invalid": "保護リストの '{entry}' はIPアドレスの正規表現ではありません。例: ^10\\.0\\.",
|
|
213
|
+
"error.ip-whitelist-entry-empty": "許可リストに空の項目があります。カンマの間を空けないでください。",
|
|
214
|
+
"error.ip-blacklist-entry-empty": "ブロックリストに空の項目があります。カンマの間を空けないでください。",
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "保護リストに空の項目があります。カンマの間を空けないでください。",
|
|
216
|
+
"field.control-no": "管理番号",
|
|
217
|
+
"field.note": "メモ",
|
|
218
|
+
"figure.label.data-bindings": "連結",
|
|
219
|
+
"text.delete": "削除",
|
|
220
|
+
"text.copy": "コピー",
|
|
221
|
+
"text.scenario": "シナリオ"
|
|
210
222
|
}
|
package/translations/ko.json
CHANGED
|
@@ -212,5 +212,11 @@
|
|
|
212
212
|
"error.ip-protectedlist-entry-invalid": "보호 목록의 '{entry}'는 IP 주소 정규식이 아닙니다. 예: ^10\\.0\\.",
|
|
213
213
|
"error.ip-whitelist-entry-empty": "허용 목록에 빈 항목이 있습니다. 쉼표 사이를 비우지 마세요.",
|
|
214
214
|
"error.ip-blacklist-entry-empty": "차단 목록에 빈 항목이 있습니다. 쉼표 사이를 비우지 마세요.",
|
|
215
|
-
"error.ip-protectedlist-entry-empty": "보호 목록에 빈 항목이 있습니다. 쉼표 사이를 비우지 마세요."
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "보호 목록에 빈 항목이 있습니다. 쉼표 사이를 비우지 마세요.",
|
|
216
|
+
"field.control-no": "관리 번호",
|
|
217
|
+
"field.note": "메모",
|
|
218
|
+
"figure.label.data-bindings": "연결",
|
|
219
|
+
"text.delete": "삭제",
|
|
220
|
+
"text.copy": "복사",
|
|
221
|
+
"text.scenario": "시나리오"
|
|
216
222
|
}
|
package/translations/ms.json
CHANGED
|
@@ -206,5 +206,17 @@
|
|
|
206
206
|
"text.tax-kind-zero-rated": "Kadar sifar",
|
|
207
207
|
"text.tax-kind-exempt": "Dikecualikan",
|
|
208
208
|
"button.rename": "Namakan Semula",
|
|
209
|
-
"button.search": "cari"
|
|
209
|
+
"button.search": "cari",
|
|
210
|
+
"error.ip-whitelist-entry-invalid": "'{entry}' dalam senarai benarkan bukan ungkapan nalar alamat IP. Contoh: ^10\\.0\\.",
|
|
211
|
+
"error.ip-blacklist-entry-invalid": "'{entry}' dalam senarai sekatan bukan ungkapan nalar alamat IP. Contoh: ^10\\.0\\.",
|
|
212
|
+
"error.ip-protectedlist-entry-invalid": "'{entry}' dalam senarai dilindungi bukan ungkapan nalar alamat IP. Contoh: ^10\\.0\\.",
|
|
213
|
+
"error.ip-whitelist-entry-empty": "Senarai benarkan mempunyai entri kosong. Jangan biarkan kosong di antara koma.",
|
|
214
|
+
"error.ip-blacklist-entry-empty": "Senarai sekatan mempunyai entri kosong. Jangan biarkan kosong di antara koma.",
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "Senarai dilindungi mempunyai entri kosong. Jangan biarkan kosong di antara koma.",
|
|
216
|
+
"field.control-no": "nombor kawalan",
|
|
217
|
+
"field.note": "nota",
|
|
218
|
+
"figure.label.data-bindings": "ikatan",
|
|
219
|
+
"text.delete": "padam",
|
|
220
|
+
"text.copy": "salin",
|
|
221
|
+
"text.scenario": "senario"
|
|
210
222
|
}
|
package/translations/zh.json
CHANGED
|
@@ -206,5 +206,17 @@
|
|
|
206
206
|
"text.tax-kind-zero-rated": "零税率",
|
|
207
207
|
"text.tax-kind-exempt": "免税",
|
|
208
208
|
"button.rename": "名称变更",
|
|
209
|
-
"button.search": "搜索"
|
|
209
|
+
"button.search": "搜索",
|
|
210
|
+
"error.ip-whitelist-entry-invalid": "允许列表中的 '{entry}' 不是 IP 地址正则表达式。例如:^10\\.0\\.",
|
|
211
|
+
"error.ip-blacklist-entry-invalid": "阻止列表中的 '{entry}' 不是 IP 地址正则表达式。例如:^10\\.0\\.",
|
|
212
|
+
"error.ip-protectedlist-entry-invalid": "保护列表中的 '{entry}' 不是 IP 地址正则表达式。例如:^10\\.0\\.",
|
|
213
|
+
"error.ip-whitelist-entry-empty": "允许列表中有空项。逗号之间不能留空。",
|
|
214
|
+
"error.ip-blacklist-entry-empty": "阻止列表中有空项。逗号之间不能留空。",
|
|
215
|
+
"error.ip-protectedlist-entry-empty": "保护列表中有空项。逗号之间不能留空。",
|
|
216
|
+
"field.control-no": "控制编号",
|
|
217
|
+
"field.note": "备注",
|
|
218
|
+
"figure.label.data-bindings": "绑定",
|
|
219
|
+
"text.delete": "删除",
|
|
220
|
+
"text.copy": "复制",
|
|
221
|
+
"text.scenario": "方案"
|
|
210
222
|
}
|