@7365admin1/layer-common 3.2.2-staging.122 → 3.2.2-staging.124
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/assets/css/primitives.css +39 -0
- package/components/EntryPass/QrTemplatePreview.vue +27 -16
- package/components/Feedback/Form.vue +26 -12
- package/components/HygieneUpdateMoreAction.vue +44 -44
- package/components/IncidentReport/affectedEntities.vue +41 -20
- package/components/NumberSettingField.vue +9 -2
- package/components/Signature.vue +40 -34
- package/components/UnitPersonCard.vue +42 -13
- package/nuxt.config.ts +6 -1
- package/package.json +22 -1
- package/plugins/vuetify.ts +37 -1
- package/utils/materialSymbols.ts +363 -0
- package/.changeset/README.md +0 -8
- package/.changeset/camera-wall-gated-endpoint.md +0 -47
- package/.changeset/camera-wall-plain-english.md +0 -60
- package/.changeset/camera-wall-selection-and-guidance.md +0 -65
- package/.changeset/config.json +0 -11
- package/.changeset/dark-primary-contrast.md +0 -39
- package/.changeset/dashboard-dark-theme-contrast.md +0 -15
- package/.changeset/notification-preferences.md +0 -17
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +0 -27
- package/.changeset/service-provider-invitation-actions.md +0 -28
- package/.editorconfig +0 -12
- package/.github/workflows/main.yml +0 -17
- package/.github/workflows/publish-staging.yml +0 -47
- package/.github/workflows/publish.yml +0 -39
- package/PUBLISHING.md +0 -269
- package/components/ScheduleTaskAreaFormDialog.vue +0 -141
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -104
- package/components/TableWithButton.vue +0 -94
- package/test/visitor-socket.test.mjs +0 -36
- package/tools/render-harness/README.md +0 -87
- package/tools/render-harness/baselines.json +0 -117
- package/tools/render-harness/harness-init.ps1 +0 -127
- package/tools/render-harness/probe.mjs +0 -229
- package/tools/render-harness/render-check.mjs +0 -306
- package/tools/render-harness/render-check.selftest.mjs +0 -129
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-dialog
|
|
3
|
-
:model-value="modelValue"
|
|
4
|
-
@update:model-value="(v) => emit('update:modelValue', v)"
|
|
5
|
-
max-width="520"
|
|
6
|
-
>
|
|
7
|
-
<!-- 520px is the handoff's `--modal-w`, and the card wears `.screen-modal`
|
|
8
|
-
for the 16px corner, the `--card` surface and the modal shadow. The
|
|
9
|
-
two `v-toolbar`s standing in for the title row and the footer were
|
|
10
|
-
fixed grey slabs that stay grey on the dark theme, and the Create
|
|
11
|
-
button was a hardcoded `color="black"` fill on a `rounded-0` square. -->
|
|
12
|
-
<v-card class="screen-modal">
|
|
13
|
-
<template v-if="$slots.header">
|
|
14
|
-
<slot
|
|
15
|
-
name="header"
|
|
16
|
-
:title="
|
|
17
|
-
mode === 'edit' ? `Edit ${entityType}` : `Add New ${entityType}`
|
|
18
|
-
"
|
|
19
|
-
:onClose="onClose"
|
|
20
|
-
/>
|
|
21
|
-
</template>
|
|
22
|
-
<v-card-title v-else class="text-capitalize">{{
|
|
23
|
-
mode === "edit" ? `Edit ${entityType}` : `Add New ${entityType}`
|
|
24
|
-
}}</v-card-title>
|
|
25
|
-
|
|
26
|
-
<v-card-text class="screen-modal__body">
|
|
27
|
-
<v-form ref="formRef" v-model="valid">
|
|
28
|
-
<v-row>
|
|
29
|
-
<v-col cols="12">
|
|
30
|
-
<InputLabel for="name" :title="label" required />
|
|
31
|
-
<!-- Still a `v-text-field`: Submit is gated on `valid`, which
|
|
32
|
-
only exists because this `:rules` runs. `.filter-field` gives
|
|
33
|
-
it the design's 40px / 10px / 13.5px shape without taking the
|
|
34
|
-
validation away. -->
|
|
35
|
-
<v-text-field
|
|
36
|
-
v-model="nameValue"
|
|
37
|
-
class="filter-field"
|
|
38
|
-
:rules="[requiredRule]"
|
|
39
|
-
/>
|
|
40
|
-
</v-col>
|
|
41
|
-
</v-row>
|
|
42
|
-
</v-form>
|
|
43
|
-
</v-card-text>
|
|
44
|
-
|
|
45
|
-
<div class="screen-modal__footer">
|
|
46
|
-
<v-btn
|
|
47
|
-
variant="outlined"
|
|
48
|
-
class="screen-btn-ghost"
|
|
49
|
-
@click="close"
|
|
50
|
-
>
|
|
51
|
-
Cancel
|
|
52
|
-
</v-btn>
|
|
53
|
-
|
|
54
|
-
<!-- Kept a `v-btn`: `loading` is the only in-flight signal this form
|
|
55
|
-
has, and `AppButton` has no equivalent. -->
|
|
56
|
-
<v-btn
|
|
57
|
-
variant="flat"
|
|
58
|
-
class="screen-btn-primary"
|
|
59
|
-
:loading="submitting"
|
|
60
|
-
@click="submit"
|
|
61
|
-
>
|
|
62
|
-
{{ mode === "edit" ? "Save" : "Create" }}
|
|
63
|
-
</v-btn>
|
|
64
|
-
</div>
|
|
65
|
-
</v-card>
|
|
66
|
-
</v-dialog>
|
|
67
|
-
</template>
|
|
68
|
-
|
|
69
|
-
<script setup lang="ts">
|
|
70
|
-
const props = defineProps({
|
|
71
|
-
modelValue: { type: Boolean, required: true },
|
|
72
|
-
mode: { type: String as PropType<"add" | "edit">, default: "add" },
|
|
73
|
-
area: { type: Object as PropType<Record<string, any> | null>, default: null },
|
|
74
|
-
label: { type: String, default: "Name" },
|
|
75
|
-
nameModel: { type: String as PropType<string | null>, default: null },
|
|
76
|
-
entityType: { type: String, default: "Area" }, // values: "Area", "Unit"
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
const emit = defineEmits([
|
|
80
|
-
"update:modelValue",
|
|
81
|
-
"saved",
|
|
82
|
-
"update:nameModel",
|
|
83
|
-
] as const);
|
|
84
|
-
|
|
85
|
-
const formRef = ref(null);
|
|
86
|
-
const valid = ref(false);
|
|
87
|
-
const submitting = ref(false);
|
|
88
|
-
|
|
89
|
-
const form = reactive({ name: "", description: "", status: "active" });
|
|
90
|
-
|
|
91
|
-
watchEffect(() => {
|
|
92
|
-
if (props.area) {
|
|
93
|
-
form.name = props.area.name || "";
|
|
94
|
-
form.description = props.area.description || "";
|
|
95
|
-
form.status = props.area.status || "active";
|
|
96
|
-
} else {
|
|
97
|
-
form.name = "";
|
|
98
|
-
form.description = "";
|
|
99
|
-
form.status = "active";
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
const { requiredRule } = useUtils();
|
|
104
|
-
|
|
105
|
-
const nameValue = computed({
|
|
106
|
-
get: () => {
|
|
107
|
-
return props.nameModel !== null ? props.nameModel : form.name;
|
|
108
|
-
},
|
|
109
|
-
set: (v: string) => {
|
|
110
|
-
if (props.nameModel !== null) {
|
|
111
|
-
emit("update:nameModel", v);
|
|
112
|
-
} else {
|
|
113
|
-
form.name = v;
|
|
114
|
-
}
|
|
115
|
-
},
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
function close() {
|
|
119
|
-
emit("update:modelValue", false);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const onClose = () => close();
|
|
123
|
-
|
|
124
|
-
async function submit() {
|
|
125
|
-
if (!valid.value) return;
|
|
126
|
-
submitting.value = true;
|
|
127
|
-
try {
|
|
128
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
129
|
-
emit("saved", { ...form, _id: props.area?._id || Date.now().toString() });
|
|
130
|
-
|
|
131
|
-
form.name = "";
|
|
132
|
-
form.description = "";
|
|
133
|
-
form.status = "active";
|
|
134
|
-
|
|
135
|
-
emit("update:modelValue", false);
|
|
136
|
-
} catch (e) {
|
|
137
|
-
} finally {
|
|
138
|
-
submitting.value = false;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
</script>
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<!-- The schedule-area copy of `Dialog/UpdateMoreAction`, converted the same
|
|
3
|
-
way: two `v-toolbar`s (fixed grey slabs that stay grey on the dark
|
|
4
|
-
theme) for the title row and the footer, and a hardcoded `color="black"`
|
|
5
|
-
fill on a square 48px tile. -->
|
|
6
|
-
<v-card class="screen-modal" width="100%">
|
|
7
|
-
<template v-if="$slots.header">
|
|
8
|
-
<slot name="header" :title="title" :onClose="onClose" />
|
|
9
|
-
</template>
|
|
10
|
-
<v-card-title v-else class="text-capitalize">{{ title }}</v-card-title>
|
|
11
|
-
|
|
12
|
-
<v-card-text class="screen-modal__body pb-0">
|
|
13
|
-
<slot name="content" />
|
|
14
|
-
</v-card-text>
|
|
15
|
-
|
|
16
|
-
<div class="screen-modal__footer">
|
|
17
|
-
<v-btn
|
|
18
|
-
variant="outlined"
|
|
19
|
-
class="screen-btn-ghost"
|
|
20
|
-
@click="emit('close')"
|
|
21
|
-
>
|
|
22
|
-
Close
|
|
23
|
-
</v-btn>
|
|
24
|
-
|
|
25
|
-
<v-menu>
|
|
26
|
-
<template #activator="{ props }">
|
|
27
|
-
<v-btn
|
|
28
|
-
variant="flat"
|
|
29
|
-
class="screen-btn-primary"
|
|
30
|
-
v-bind="props"
|
|
31
|
-
>
|
|
32
|
-
More actions
|
|
33
|
-
</v-btn>
|
|
34
|
-
</template>
|
|
35
|
-
|
|
36
|
-
<!-- Dropdown shape left as Vuetify draws it - the handoff specifies
|
|
37
|
-
the profile dropdown only. `text-red` (Vuetify's fixed #f44336,
|
|
38
|
-
the same red on both themes) becomes `text-error`, which is
|
|
39
|
-
`--v-theme-error`, i.e. the design's `--err`, matching the
|
|
40
|
-
handoff's "red Logout row (err color, err-bg hover)". -->
|
|
41
|
-
<v-list class="pa-0 area-more__list">
|
|
42
|
-
<v-list-item v-if="canUpdate" @click="emit('edit')">
|
|
43
|
-
<v-list-item-title>
|
|
44
|
-
{{ editButtonLabel }}
|
|
45
|
-
</v-list-item-title>
|
|
46
|
-
</v-list-item>
|
|
47
|
-
|
|
48
|
-
<v-list-item
|
|
49
|
-
v-if="canDelete"
|
|
50
|
-
@click="emit('delete')"
|
|
51
|
-
class="text-error"
|
|
52
|
-
>
|
|
53
|
-
<v-list-item-title>
|
|
54
|
-
{{ deleteButtonLabel }}
|
|
55
|
-
</v-list-item-title>
|
|
56
|
-
</v-list-item>
|
|
57
|
-
</v-list>
|
|
58
|
-
</v-menu>
|
|
59
|
-
</div>
|
|
60
|
-
</v-card>
|
|
61
|
-
</template>
|
|
62
|
-
|
|
63
|
-
<script setup lang="ts">
|
|
64
|
-
const prop = defineProps({
|
|
65
|
-
canUpdate: {
|
|
66
|
-
type: Boolean,
|
|
67
|
-
default: true,
|
|
68
|
-
},
|
|
69
|
-
canDelete: {
|
|
70
|
-
type: Boolean,
|
|
71
|
-
default: true,
|
|
72
|
-
},
|
|
73
|
-
editButtonLabel: {
|
|
74
|
-
type: String,
|
|
75
|
-
default: "Edit Area",
|
|
76
|
-
},
|
|
77
|
-
deleteButtonLabel: {
|
|
78
|
-
type: String,
|
|
79
|
-
default: "Delete Area",
|
|
80
|
-
},
|
|
81
|
-
title: {
|
|
82
|
-
type: String,
|
|
83
|
-
default: "Area Actions",
|
|
84
|
-
},
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
const emit = defineEmits(["close", "manage", "edit", "delete"]);
|
|
88
|
-
const {
|
|
89
|
-
canUpdate,
|
|
90
|
-
editButtonLabel,
|
|
91
|
-
deleteButtonLabel,
|
|
92
|
-
manageButtonLabel,
|
|
93
|
-
title,
|
|
94
|
-
} = prop;
|
|
95
|
-
</script>
|
|
96
|
-
|
|
97
|
-
<style scoped>
|
|
98
|
-
/* `text-subtitle-2` was Vuetify's 0.875rem/500; the handoff names no type for
|
|
99
|
-
a menu row, so it takes the established cell type. */
|
|
100
|
-
.area-more__list :deep(.v-list-item-title) {
|
|
101
|
-
font-size: var(--fs-cell);
|
|
102
|
-
font-weight: var(--fw-cell);
|
|
103
|
-
}
|
|
104
|
-
</style>
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<v-card flat border="sm black" :loading="loading" class="px-0" :ripple="false">
|
|
3
|
-
<v-toolbar class="px-5">
|
|
4
|
-
<template #prepend>
|
|
5
|
-
<span class="text-subtitle-1 font-weight-black">{{ tableTitle }}</span>
|
|
6
|
-
</template>
|
|
7
|
-
<template #append>
|
|
8
|
-
<v-btn color="primary" variant="flat" size="large" density="comfortable" :text="buttonLabel"
|
|
9
|
-
class="text-capitalize" />
|
|
10
|
-
</template>
|
|
11
|
-
</v-toolbar>
|
|
12
|
-
<v-card-text class="pa-0 bg-red">
|
|
13
|
-
<v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
|
|
14
|
-
fixed-header hide-default-footer hide-default-header @click:row="$emit('row-click', $event)"
|
|
15
|
-
style="max-height: calc(100vh - (200px))">
|
|
16
|
-
<template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
|
|
17
|
-
<slot :name="slotName" v-bind="slotProps" />
|
|
18
|
-
</template>
|
|
19
|
-
</v-data-table>
|
|
20
|
-
</v-card-text>
|
|
21
|
-
</v-card>
|
|
22
|
-
</template>
|
|
23
|
-
|
|
24
|
-
<script setup lang="ts">
|
|
25
|
-
const selected = defineModel({
|
|
26
|
-
type: Array as PropType<Array<string>>,
|
|
27
|
-
default: () => [],
|
|
28
|
-
});
|
|
29
|
-
const page = defineModel("page", { type: Number, default: 0 });
|
|
30
|
-
|
|
31
|
-
const props = defineProps({
|
|
32
|
-
headers: {
|
|
33
|
-
type: Array as PropType<Array<Record<string, string | number>>>,
|
|
34
|
-
required: true,
|
|
35
|
-
default: () => [],
|
|
36
|
-
},
|
|
37
|
-
items: {
|
|
38
|
-
type: Array as PropType<Array<Record<string, any>>>,
|
|
39
|
-
required: true,
|
|
40
|
-
default: () => [],
|
|
41
|
-
},
|
|
42
|
-
|
|
43
|
-
itemValue: {
|
|
44
|
-
type: String,
|
|
45
|
-
default: "_id",
|
|
46
|
-
},
|
|
47
|
-
itemsPerPage: {
|
|
48
|
-
type: Number,
|
|
49
|
-
default: 20,
|
|
50
|
-
},
|
|
51
|
-
loading: {
|
|
52
|
-
type: Boolean,
|
|
53
|
-
required: true,
|
|
54
|
-
default: true,
|
|
55
|
-
},
|
|
56
|
-
tableTitle: {
|
|
57
|
-
type: String,
|
|
58
|
-
default: "",
|
|
59
|
-
required: false
|
|
60
|
-
},
|
|
61
|
-
buttonLabel: {
|
|
62
|
-
type: String,
|
|
63
|
-
default: "Add",
|
|
64
|
-
required: false
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const emit = defineEmits(["refresh", "update:pagination", "row-click"]);
|
|
69
|
-
|
|
70
|
-
function increment() {
|
|
71
|
-
page.value++;
|
|
72
|
-
emit("update:pagination", page.value);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function decrement() {
|
|
76
|
-
page.value--;
|
|
77
|
-
emit("update:pagination", page.value);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function tableRowClickHandler(_: any, row: any) {
|
|
81
|
-
const item = props.items[row.index];
|
|
82
|
-
emit("row-click", item);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
const selectAll = ref(false);
|
|
86
|
-
|
|
87
|
-
watch(selectAll, (curr) => {
|
|
88
|
-
selected.value.splice(0, selected.value.length);
|
|
89
|
-
if (curr) {
|
|
90
|
-
const ids = props.items.map((i) => i._id as string);
|
|
91
|
-
selected.value.push(...ids);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
</script>
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import assert from "node:assert/strict";
|
|
2
|
-
import { test } from "node:test";
|
|
3
|
-
|
|
4
|
-
import { cameraAlertMessage } from "./.build/useVisitorSocket.mjs";
|
|
5
|
-
|
|
6
|
-
/*
|
|
7
|
-
* The reported symptom was one camera raising a red toast every 10 seconds on
|
|
8
|
-
* an unrelated page. API-core now paces the alert; this side has to make sure
|
|
9
|
-
* several cameras failing at once still cannot become several snackbars.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
test("nothing is shown when no camera is down", () => {
|
|
13
|
-
assert.equal(cameraAlertMessage([]), "");
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
test("one failing camera shows the server's own message, which names it", () => {
|
|
17
|
-
const text = "The entry and exit ANPR camera at Seventh Condominium is not responding.";
|
|
18
|
-
assert.equal(cameraAlertMessage([text]), text);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
test("several failing cameras collapse into a single line", () => {
|
|
22
|
-
const many = ["camera one is down", "camera two is down", "camera three is down"];
|
|
23
|
-
const text = cameraAlertMessage(many);
|
|
24
|
-
assert.match(text, /^3 cameras at this site are not responding/);
|
|
25
|
-
for (const message of many) assert.doesNotMatch(text, new RegExp(message));
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
test("the collapsed line points at the page that lists them", () => {
|
|
29
|
-
assert.match(cameraAlertMessage(["a", "b"]), /Site Settings > Cameras/);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("no camera message tells the operator to deactivate anything", () => {
|
|
33
|
-
for (const messages of [["a camera is down"], ["a", "b"]]) {
|
|
34
|
-
assert.doesNotMatch(cameraAlertMessage(messages), /inactive|deactivat/i);
|
|
35
|
-
}
|
|
36
|
-
});
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# Render harness hardening
|
|
2
|
-
|
|
3
|
-
Tooling, not product code. Nothing here ships in `@7365admin1/layer-common` and
|
|
4
|
-
nothing here is imported by any component — it exists to stop the theme-review
|
|
5
|
-
harness reporting a pass it has not earned.
|
|
6
|
-
|
|
7
|
-
The harness itself (vite + Vuetify + a mirror of this package, driven by
|
|
8
|
-
Playwright) lives outside the repo, under
|
|
9
|
-
`_scratch-theme-review/harness`. These four files attach to it.
|
|
10
|
-
|
|
11
|
-
| File | What it does |
|
|
12
|
-
|---|---|
|
|
13
|
-
| `harness-init.ps1` | Makes a private harness for one worktree: fork directory and dev port both derived from the worktree path, own vite cache, mirror stamped with what was synced. |
|
|
14
|
-
| `render-check.mjs` | The run. Refuses to measure an unprovable mirror, then measures each screen at 360/768/1024/1440 × light/dark. |
|
|
15
|
-
| `probe.mjs` | The in-page half — what is actually read off the glass. |
|
|
16
|
-
| `render-check.selftest.mjs` | Proves every detector fails the scenario it was written for. `node render-check.selftest.mjs`, no server needed. |
|
|
17
|
-
| `baselines.json` | Per-screen recorded element and painted-text counts. |
|
|
18
|
-
|
|
19
|
-
## Use
|
|
20
|
-
|
|
21
|
-
```powershell
|
|
22
|
-
cd <worktree>
|
|
23
|
-
..\iservice365-layer-common\tools\render-harness\harness-init.ps1 # prints fork + port
|
|
24
|
-
Start-Process -WorkingDirectory <fork> cmd.exe -ArgumentList '/c','npx vite --port <port> --strictPort'
|
|
25
|
-
cd <fork>; $env:PORT=<port>
|
|
26
|
-
node render-check.mjs <label> CameraMain NotificationSettings
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
Re-run `harness-init.ps1` after any edit to `assets`, `components`,
|
|
30
|
-
`composables`, `constants`, `types` or `utils` — `render-check.mjs` compares the
|
|
31
|
-
mirror to the worktree file by file and refuses to run when they disagree.
|
|
32
|
-
|
|
33
|
-
## What each guard is for
|
|
34
|
-
|
|
35
|
-
Every one of these was a real false pass on 2026-08-14, not a hypothetical.
|
|
36
|
-
|
|
37
|
-
- **Shared mirror.** One `lc/` copy meant two agents silently measured each
|
|
38
|
-
other's branch. Isolation is now derived from the worktree, so there is
|
|
39
|
-
nothing to remember, and the mirror carries a stamp the run checks.
|
|
40
|
-
- **Empty state.** A panel scored 8/8 dark-clean while drawing "No access logs
|
|
41
|
-
found". A table was signed off with zero header columns and "No data
|
|
42
|
-
available", so the header and cell rules under review had never been drawn. A
|
|
43
|
-
screen showing its empty state is a fixture bug and fails.
|
|
44
|
-
- **Empty form.** The same bug in a shape nothing else can see — 1112 elements,
|
|
45
|
-
nothing blank, and not one control holding a value.
|
|
46
|
-
- **Blank screen.** Six elements, zero page errors, and a counter read it as a
|
|
47
|
-
pass. Painted text is the honest signal.
|
|
48
|
-
- **Element counts.** `MIN_ELS = 20` was at once too low (a cold partial paint
|
|
49
|
-
at 32 scored ok where warm is 49) and too high (a drawing screen at 19 was
|
|
50
|
-
reported NOT-MOUNTED). Screens legitimately range from 3 to 1100 elements, so
|
|
51
|
-
each carries its own baseline and an un-baselined screen is refused rather
|
|
52
|
-
than guessed at.
|
|
53
|
-
- **Cold compile.** The run waits for the DOM to stop growing instead of a fixed
|
|
54
|
-
timeout, warms every screen first, prints the discarded warm-up numbers so the
|
|
55
|
-
gap is visible, and re-measures any row well under that screen's own best.
|
|
56
|
-
- **Undefined custom properties.** `var(--success)` where the token is `--ok`:
|
|
57
|
-
the declaration is invalid, the element inherits, and nothing reports it. Now
|
|
58
|
-
caught even when the rule matches nothing on the screen — which is how the
|
|
59
|
-
original survived a full 8-shot render.
|
|
60
|
-
- **Disabled controls.** WCAG-1.4.3-exempt, so logging them as failures put
|
|
61
|
-
fictional entries in an AA ledger. Classified as exempt, and the enabled state
|
|
62
|
-
is probed separately so the exemption cannot hide a real failure.
|
|
63
|
-
- **Vuetify defaults.** `VAutocomplete` was missing from the harness, and 19
|
|
64
|
-
components in this package use one — the run would have invented a defect the
|
|
65
|
-
product does not have. The defaults are diffed against `plugins/vuetify.ts`
|
|
66
|
-
every run.
|
|
67
|
-
|
|
68
|
-
## Baselines
|
|
69
|
-
|
|
70
|
-
`--record` writes the current run's element and painted-text counts:
|
|
71
|
-
|
|
72
|
-
```powershell
|
|
73
|
-
node render-check.mjs --record base CameraMain
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
Only record from a run whose **screenshots you have opened**. A baseline
|
|
77
|
-
recorded from a broken render makes the break permanent. A conversion that
|
|
78
|
-
genuinely shrinks a screen below 70% of its baseline will flag — look at the
|
|
79
|
-
shot, then re-record.
|
|
80
|
-
|
|
81
|
-
`baselines.json` accepts `"allowEmpty": true` per screen for the rare component
|
|
82
|
-
whose empty state IS the thing under review.
|
|
83
|
-
|
|
84
|
-
## Rule that does not bend
|
|
85
|
-
|
|
86
|
-
Fixtures are harness-only. Package code is never bent to suit the harness. If a
|
|
87
|
-
screen will not draw, the fixture is wrong.
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"CameraMain": {
|
|
3
|
-
"els": 55,
|
|
4
|
-
"textChars": 85,
|
|
5
|
-
"recordedFrom": "0642279"
|
|
6
|
-
},
|
|
7
|
-
"NotificationSettings": {
|
|
8
|
-
"els": 150,
|
|
9
|
-
"textChars": 485,
|
|
10
|
-
"recordedFrom": "0642279"
|
|
11
|
-
},
|
|
12
|
-
"OvernightParkingAvailability": {
|
|
13
|
-
"els": 1132,
|
|
14
|
-
"textChars": 385,
|
|
15
|
-
"recordedFrom": "0642279"
|
|
16
|
-
},
|
|
17
|
-
"BulletinExpirationChip": {
|
|
18
|
-
"els": 3,
|
|
19
|
-
"textChars": 2,
|
|
20
|
-
"recordedFrom": "0642279"
|
|
21
|
-
},
|
|
22
|
-
"AccessCardAssignToUnitForm": {
|
|
23
|
-
"els": 177,
|
|
24
|
-
"textChars": 131,
|
|
25
|
-
"recordedFrom": "178ebf0"
|
|
26
|
-
},
|
|
27
|
-
"AccessCardReplaceForm": {
|
|
28
|
-
"els": 68,
|
|
29
|
-
"textChars": 83,
|
|
30
|
-
"recordedFrom": "178ebf0"
|
|
31
|
-
},
|
|
32
|
-
"AddEqupmentForm": {
|
|
33
|
-
"els": 90,
|
|
34
|
-
"textChars": 99,
|
|
35
|
-
"recordedFrom": "178ebf0"
|
|
36
|
-
},
|
|
37
|
-
"BuildingForm": {
|
|
38
|
-
"els": 126,
|
|
39
|
-
"textChars": 69,
|
|
40
|
-
"recordedFrom": "178ebf0"
|
|
41
|
-
},
|
|
42
|
-
"BulletinBoardForm": {
|
|
43
|
-
"els": 172,
|
|
44
|
-
"textChars": 165,
|
|
45
|
-
"recordedFrom": "178ebf0"
|
|
46
|
-
},
|
|
47
|
-
"CameraForm": {
|
|
48
|
-
"els": 127,
|
|
49
|
-
"textChars": 80,
|
|
50
|
-
"recordedFrom": "178ebf0"
|
|
51
|
-
},
|
|
52
|
-
"DocumentForm": {
|
|
53
|
-
"els": 52,
|
|
54
|
-
"textChars": 57,
|
|
55
|
-
"recordedFrom": "178ebf0"
|
|
56
|
-
},
|
|
57
|
-
"InvitationClientForm": {
|
|
58
|
-
"els": 101,
|
|
59
|
-
"textChars": 47,
|
|
60
|
-
"recordedFrom": "178ebf0"
|
|
61
|
-
},
|
|
62
|
-
"InvitationForm": {
|
|
63
|
-
"els": 86,
|
|
64
|
-
"textChars": 43,
|
|
65
|
-
"recordedFrom": "178ebf0"
|
|
66
|
-
},
|
|
67
|
-
"ServiceProviderFormCreate": {
|
|
68
|
-
"els": 51,
|
|
69
|
-
"textChars": 38,
|
|
70
|
-
"recordedFrom": "178ebf0"
|
|
71
|
-
},
|
|
72
|
-
"AcceptDialog": {
|
|
73
|
-
"els": 24,
|
|
74
|
-
"textChars": 106,
|
|
75
|
-
"recordedFrom": "178ebf0"
|
|
76
|
-
},
|
|
77
|
-
"Card/DeleteConfirmation": {
|
|
78
|
-
"els": 21,
|
|
79
|
-
"textChars": 129,
|
|
80
|
-
"recordedFrom": "178ebf0"
|
|
81
|
-
},
|
|
82
|
-
"Dialog/DeleteConfirmation": {
|
|
83
|
-
"els": 21,
|
|
84
|
-
"textChars": 129,
|
|
85
|
-
"recordedFrom": "178ebf0"
|
|
86
|
-
},
|
|
87
|
-
"HidAccessLogDashboard": {
|
|
88
|
-
"els": 172,
|
|
89
|
-
"textChars": 437,
|
|
90
|
-
"recordedFrom": "178ebf0"
|
|
91
|
-
},
|
|
92
|
-
"HidIntercomManagement": {
|
|
93
|
-
"els": 148,
|
|
94
|
-
"textChars": 167,
|
|
95
|
-
"recordedFrom": "178ebf0"
|
|
96
|
-
},
|
|
97
|
-
"HidUserEnrollment": {
|
|
98
|
-
"els": 142,
|
|
99
|
-
"textChars": 287,
|
|
100
|
-
"recordedFrom": "178ebf0"
|
|
101
|
-
},
|
|
102
|
-
"HidReaderManagement": {
|
|
103
|
-
"els": 112,
|
|
104
|
-
"textChars": 377,
|
|
105
|
-
"recordedFrom": "178ebf0"
|
|
106
|
-
},
|
|
107
|
-
"HidQrCodeConfiguration": {
|
|
108
|
-
"els": 106,
|
|
109
|
-
"textChars": 453,
|
|
110
|
-
"recordedFrom": "178ebf0"
|
|
111
|
-
},
|
|
112
|
-
"HidServiceSettingsPanel": {
|
|
113
|
-
"els": 18,
|
|
114
|
-
"textChars": 164,
|
|
115
|
-
"recordedFrom": "178ebf0"
|
|
116
|
-
}
|
|
117
|
-
}
|