inspector-ng 0.0.7 → 0.0.9
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/README.md +15 -28
- package/esm2022/lib/checkpoint-search.mjs +50 -0
- package/esm2022/lib/checkpoints.mjs +254 -198
- package/esm2022/lib/inspector.component.mjs +199 -20
- package/esm2022/public-api.mjs +2 -2
- package/fesm2022/inspector-ng.mjs +502 -218
- package/fesm2022/inspector-ng.mjs.map +1 -1
- package/lib/checkpoint-search.d.ts +3 -0
- package/lib/checkpoints.d.ts +46 -46
- package/lib/inspector.component.d.ts +29 -8
- package/package.json +2 -1
- package/public-api.d.ts +1 -1
|
@@ -1,274 +1,330 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import {
|
|
2
|
+
import { InjectionToken, PLATFORM_ID, Inject, Injectable, signal, Optional, makeEnvironmentProviders, viewChild, inject, computed, effect, HostListener, Input, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { isPlatformBrowser } from '@angular/common';
|
|
4
|
-
import * as
|
|
4
|
+
import * as i2 from '@angular/router';
|
|
5
5
|
import { Router } from '@angular/router';
|
|
6
|
+
import * as i1 from '@ngrx/store';
|
|
7
|
+
import { Store, META_REDUCERS } from '@ngrx/store';
|
|
8
|
+
import { firstValueFrom, take } from 'rxjs';
|
|
6
9
|
|
|
7
10
|
const inspector_STORAGE_KEY = "inspector-state";
|
|
8
11
|
const inspector_STATE_VERSION = 2;
|
|
9
12
|
const GUIDE_SNAP_DISTANCE = 10;
|
|
10
13
|
const GUIDE_HITBOX_SIZE = 14;
|
|
11
14
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
const RESTORE_CHECKPOINT = '[Inspector Checkpoints] Restore';
|
|
16
|
+
const DATABASE_NAME = 'inspector-ng';
|
|
17
|
+
const DATABASE_VERSION = 1;
|
|
18
|
+
const STORE_NAME = 'checkpoints';
|
|
19
|
+
const INSPECTOR_CHECKPOINT_REPOSITORY = new InjectionToken('INSPECTOR_CHECKPOINT_REPOSITORY');
|
|
20
|
+
function inspectorCheckpointMetaReducer(reducer) {
|
|
21
|
+
return (state, action) => {
|
|
22
|
+
if (action.type === RESTORE_CHECKPOINT) {
|
|
23
|
+
return action.state;
|
|
24
|
+
}
|
|
25
|
+
return reducer(state, action);
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
class InspectorCheckpointRepository {
|
|
29
|
+
}
|
|
30
|
+
class IndexedDbCheckpointRepository extends InspectorCheckpointRepository {
|
|
31
|
+
constructor(platformId) {
|
|
32
|
+
super();
|
|
33
|
+
this.platformId = platformId;
|
|
34
|
+
this.databasePromise = null;
|
|
35
|
+
}
|
|
36
|
+
async list() {
|
|
37
|
+
const database = await this.database();
|
|
38
|
+
const values = await this.request(database.transaction(STORE_NAME, 'readonly').objectStore(STORE_NAME).getAll());
|
|
39
|
+
return values
|
|
40
|
+
.filter(isCheckpointRecord)
|
|
41
|
+
.sort(newestFirst$1);
|
|
42
|
+
}
|
|
43
|
+
async put(checkpoint) {
|
|
44
|
+
const database = await this.database();
|
|
45
|
+
await this.transactionComplete(database, 'readwrite', (store) => store.put(checkpoint));
|
|
46
|
+
}
|
|
47
|
+
async delete(id) {
|
|
48
|
+
const database = await this.database();
|
|
49
|
+
await this.transactionComplete(database, 'readwrite', (store) => store.delete(id));
|
|
50
|
+
}
|
|
51
|
+
database() {
|
|
52
|
+
if (!isPlatformBrowser(this.platformId) || typeof indexedDB === 'undefined') {
|
|
53
|
+
return Promise.reject(new Error('Checkpoints are only available in a browser with IndexedDB.'));
|
|
54
|
+
}
|
|
55
|
+
if (this.databasePromise)
|
|
56
|
+
return this.databasePromise;
|
|
57
|
+
this.databasePromise = new Promise((resolve, reject) => {
|
|
58
|
+
const request = indexedDB.open(DATABASE_NAME, DATABASE_VERSION);
|
|
59
|
+
request.onupgradeneeded = () => {
|
|
60
|
+
const database = request.result;
|
|
61
|
+
if (database.objectStoreNames.contains(STORE_NAME))
|
|
62
|
+
return;
|
|
63
|
+
const store = database.createObjectStore(STORE_NAME, { keyPath: 'id' });
|
|
64
|
+
store.createIndex('createdAt', 'createdAt');
|
|
65
|
+
};
|
|
66
|
+
request.onsuccess = () => resolve(request.result);
|
|
67
|
+
request.onerror = () => reject(request.error ?? new Error('IndexedDB could not be opened.'));
|
|
68
|
+
request.onblocked = () => reject(new Error('IndexedDB is blocked by another open tab.'));
|
|
69
|
+
});
|
|
70
|
+
return this.databasePromise;
|
|
71
|
+
}
|
|
72
|
+
request(request) {
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
request.onsuccess = () => resolve(request.result);
|
|
75
|
+
request.onerror = () => reject(request.error ?? new Error('IndexedDB request failed.'));
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
transactionComplete(database, mode, operation) {
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const transaction = database.transaction(STORE_NAME, mode);
|
|
81
|
+
transaction.oncomplete = () => resolve();
|
|
82
|
+
transaction.onerror = () => reject(transaction.error ?? new Error('IndexedDB transaction failed.'));
|
|
83
|
+
transaction.onabort = () => reject(transaction.error ?? new Error('IndexedDB transaction was aborted.'));
|
|
84
|
+
operation(transaction.objectStore(STORE_NAME));
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IndexedDbCheckpointRepository, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
88
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IndexedDbCheckpointRepository }); }
|
|
89
|
+
}
|
|
90
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: IndexedDbCheckpointRepository, decorators: [{
|
|
91
|
+
type: Injectable
|
|
92
|
+
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
93
|
+
type: Inject,
|
|
94
|
+
args: [PLATFORM_ID]
|
|
95
|
+
}] }] });
|
|
96
|
+
class InspectorCheckpointService {
|
|
97
|
+
constructor(repository, store, router, platformId) {
|
|
98
|
+
this.repository = repository;
|
|
99
|
+
this.store = store;
|
|
15
100
|
this.router = router;
|
|
101
|
+
this.platformId = platformId;
|
|
16
102
|
this.checkpoints = signal([]);
|
|
17
|
-
this.
|
|
103
|
+
this.loading = signal(false);
|
|
104
|
+
this.busy = signal(false);
|
|
18
105
|
this.error = signal(null);
|
|
19
|
-
this.warning = signal(null);
|
|
20
|
-
this.totalBytes = computed(() => this.checkpoints().reduce((total, checkpoint) => total + checkpoint.sizeBytes, 0));
|
|
21
|
-
this.adapters = new Map();
|
|
22
|
-
this.remoteLoaders = new Map();
|
|
23
|
-
this.isCheckpoint = (value) => {
|
|
24
|
-
if (!value || typeof value !== 'object')
|
|
25
|
-
return false;
|
|
26
|
-
const checkpoint = value;
|
|
27
|
-
return checkpoint.version === 1 && typeof checkpoint.id === 'string' &&
|
|
28
|
-
typeof checkpoint.name === 'string' && typeof checkpoint.url === 'string' &&
|
|
29
|
-
typeof checkpoint.createdAt === 'string' && Array.isArray(checkpoint.scopes) &&
|
|
30
|
-
!!checkpoint.state && typeof checkpoint.state === 'object' && typeof checkpoint.sizeBytes === 'number';
|
|
31
|
-
};
|
|
32
|
-
this.isBrowser = isPlatformBrowser(platformId);
|
|
33
|
-
this.hydrate();
|
|
34
106
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
107
|
+
async load() {
|
|
108
|
+
this.error.set(null);
|
|
109
|
+
this.loading.set(true);
|
|
110
|
+
try {
|
|
111
|
+
this.checkpoints.set((await this.repository.list()).sort(newestFirst$1));
|
|
38
112
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
113
|
+
catch (error) {
|
|
114
|
+
this.error.set(this.messageFor(error, 'Saved checkpoints could not be loaded. Try reloading the page.'));
|
|
115
|
+
}
|
|
116
|
+
finally {
|
|
117
|
+
this.loading.set(false);
|
|
43
118
|
}
|
|
44
|
-
this.adapters.set(adapter.id, adapter);
|
|
45
|
-
return () => this.adapters.delete(adapter.id);
|
|
46
|
-
}
|
|
47
|
-
registerRemoteScope(scope, loader) {
|
|
48
|
-
this.remoteLoaders.set(scope, loader);
|
|
49
|
-
return () => this.remoteLoaders.delete(scope);
|
|
50
119
|
}
|
|
51
|
-
async save(
|
|
120
|
+
async save() {
|
|
52
121
|
this.error.set(null);
|
|
53
|
-
if (!this.
|
|
54
|
-
this.error.set(
|
|
122
|
+
if (!this.store) {
|
|
123
|
+
this.error.set(this.missingStoreMessage());
|
|
55
124
|
return null;
|
|
56
125
|
}
|
|
57
|
-
this.
|
|
126
|
+
this.busy.set(true);
|
|
58
127
|
try {
|
|
59
|
-
const
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const
|
|
128
|
+
const currentState = await firstValueFrom(this.store.pipe(take(1)));
|
|
129
|
+
const state = cloneJson(currentState);
|
|
130
|
+
const persisted = await this.repository.list();
|
|
131
|
+
const route = this.currentRoute();
|
|
132
|
+
const pathname = this.currentPathname();
|
|
64
133
|
const checkpoint = {
|
|
65
134
|
version: 1,
|
|
66
|
-
id:
|
|
67
|
-
name:
|
|
68
|
-
|
|
69
|
-
createdAt,
|
|
70
|
-
scopes: this.getScopes(Object.keys(state)),
|
|
135
|
+
id: createCheckpointId(),
|
|
136
|
+
name: nextAutomaticName(pathname, persisted.map(({ name }) => name)),
|
|
137
|
+
route,
|
|
138
|
+
createdAt: new Date().toISOString(),
|
|
71
139
|
state,
|
|
72
|
-
sizeBytes: 0,
|
|
73
140
|
};
|
|
74
|
-
|
|
75
|
-
this.
|
|
76
|
-
return
|
|
141
|
+
await this.repository.put(checkpoint);
|
|
142
|
+
this.checkpoints.set([checkpoint, ...persisted].sort(newestFirst$1));
|
|
143
|
+
return checkpoint;
|
|
77
144
|
}
|
|
78
145
|
catch (error) {
|
|
79
|
-
this.error.set(this.
|
|
146
|
+
this.error.set(this.storageMessage(error, 'The checkpoint could not be saved.'));
|
|
80
147
|
return null;
|
|
81
148
|
}
|
|
82
149
|
finally {
|
|
83
|
-
this.
|
|
150
|
+
this.busy.set(false);
|
|
84
151
|
}
|
|
85
152
|
}
|
|
86
153
|
async restore(id) {
|
|
87
|
-
const checkpoint = this.checkpoints().find((item) => item.id === id);
|
|
88
154
|
this.error.set(null);
|
|
89
|
-
this.
|
|
90
|
-
|
|
91
|
-
this.error.set('Checkpoint not found.');
|
|
155
|
+
if (!this.store) {
|
|
156
|
+
this.error.set(this.missingStoreMessage());
|
|
92
157
|
return false;
|
|
93
158
|
}
|
|
94
|
-
this.
|
|
159
|
+
this.busy.set(true);
|
|
95
160
|
try {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
for (const [adapterId, value] of Object.entries(checkpoint.state)) {
|
|
104
|
-
const adapter = this.adapters.get(adapterId);
|
|
105
|
-
if (!adapter) {
|
|
106
|
-
missing.push(adapterId);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
await adapter.restore(this.cloneValue(value, adapterId));
|
|
161
|
+
const checkpoint = (await this.repository.list()).find((item) => item.id === id);
|
|
162
|
+
if (!checkpoint)
|
|
163
|
+
throw new Error('Checkpoint not found. It may have been deleted in another tab.');
|
|
164
|
+
const state = cloneJson(checkpoint.state, `“${checkpoint.name}” could not be decoded. Delete it and save a new checkpoint.`);
|
|
165
|
+
this.store.dispatch({ type: RESTORE_CHECKPOINT, state });
|
|
166
|
+
if (this.router && !await this.router.navigateByUrl(checkpoint.route)) {
|
|
167
|
+
throw new Error(`The state was restored, but navigation to “${checkpoint.route}” was cancelled.`);
|
|
110
168
|
}
|
|
111
|
-
if (missing.length) {
|
|
112
|
-
this.warning.set(`Skipped ${missing.length} unavailable adapter${missing.length === 1 ? '' : 's'}: ${missing.join(', ')}.`);
|
|
113
|
-
}
|
|
114
|
-
await this.router.navigateByUrl(checkpoint.url);
|
|
115
169
|
return true;
|
|
116
170
|
}
|
|
117
171
|
catch (error) {
|
|
118
|
-
this.error.set(this.messageFor(error, '
|
|
172
|
+
this.error.set(this.messageFor(error, 'The checkpoint could not be restored. Delete it and save a new one.'));
|
|
119
173
|
return false;
|
|
120
174
|
}
|
|
121
175
|
finally {
|
|
122
|
-
this.
|
|
176
|
+
this.busy.set(false);
|
|
123
177
|
}
|
|
124
178
|
}
|
|
125
|
-
rename(id, name) {
|
|
179
|
+
async rename(id, name) {
|
|
180
|
+
this.error.set(null);
|
|
126
181
|
const nextName = name.trim();
|
|
127
182
|
if (!nextName) {
|
|
128
|
-
this.error.set('
|
|
129
|
-
return;
|
|
130
|
-
}
|
|
131
|
-
this.error.set(null);
|
|
132
|
-
const next = this.checkpoints().map((checkpoint) => checkpoint.id === id ? { ...checkpoint, name: nextName } : checkpoint);
|
|
133
|
-
this.persistWithEviction(next);
|
|
134
|
-
}
|
|
135
|
-
delete(id) {
|
|
136
|
-
this.error.set(null);
|
|
137
|
-
this.persistWithEviction(this.checkpoints().filter((checkpoint) => checkpoint.id !== id));
|
|
138
|
-
}
|
|
139
|
-
hydrate() {
|
|
140
|
-
if (!this.isBrowser) {
|
|
141
|
-
return;
|
|
183
|
+
this.error.set('Please enter a checkpoint name.');
|
|
184
|
+
return false;
|
|
142
185
|
}
|
|
186
|
+
this.busy.set(true);
|
|
143
187
|
try {
|
|
144
|
-
const
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
const parsed = JSON.parse(raw);
|
|
148
|
-
if (!Array.isArray(parsed))
|
|
149
|
-
return;
|
|
150
|
-
const checkpoints = parsed.filter(this.isCheckpoint);
|
|
151
|
-
const normalized = this.normalizeLegacyNames(checkpoints);
|
|
152
|
-
const sorted = normalized.checkpoints.sort((a, b) => b.createdAt.localeCompare(a.createdAt) || b.id.localeCompare(a.id));
|
|
153
|
-
this.checkpoints.set(sorted);
|
|
154
|
-
if (normalized.changed) {
|
|
155
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(sorted));
|
|
188
|
+
const checkpoints = await this.repository.list();
|
|
189
|
+
if (checkpoints.some((item) => item.id !== id && item.name.toLocaleLowerCase() === nextName.toLocaleLowerCase())) {
|
|
190
|
+
throw new Error(`A checkpoint named “${nextName}” already exists. Choose a different name.`);
|
|
156
191
|
}
|
|
192
|
+
const checkpoint = checkpoints.find((item) => item.id === id);
|
|
193
|
+
if (!checkpoint)
|
|
194
|
+
throw new Error('Checkpoint not found. It may have been deleted in another tab.');
|
|
195
|
+
await this.repository.put({ ...checkpoint, name: nextName });
|
|
196
|
+
this.checkpoints.set(checkpoints.map((item) => item.id === id ? { ...item, name: nextName } : item).sort(newestFirst$1));
|
|
197
|
+
return true;
|
|
157
198
|
}
|
|
158
|
-
catch {
|
|
159
|
-
this.
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.error.set(this.storageMessage(error, 'The checkpoint could not be renamed.'));
|
|
201
|
+
return false;
|
|
160
202
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (!this.isBrowser)
|
|
164
|
-
return;
|
|
165
|
-
let candidate = checkpoints;
|
|
166
|
-
while (true) {
|
|
167
|
-
try {
|
|
168
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(candidate));
|
|
169
|
-
this.checkpoints.set(candidate);
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
catch (error) {
|
|
173
|
-
if (!this.isQuotaError(error) || candidate.length === 0) {
|
|
174
|
-
throw error;
|
|
175
|
-
}
|
|
176
|
-
candidate = candidate.slice(0, -1);
|
|
177
|
-
this.warning.set('Storage was full; the oldest checkpoint was removed.');
|
|
178
|
-
}
|
|
203
|
+
finally {
|
|
204
|
+
this.busy.set(false);
|
|
179
205
|
}
|
|
180
206
|
}
|
|
181
|
-
|
|
207
|
+
async delete(id) {
|
|
208
|
+
this.error.set(null);
|
|
209
|
+
this.busy.set(true);
|
|
182
210
|
try {
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
return JSON.parse(serialized);
|
|
211
|
+
await this.repository.delete(id);
|
|
212
|
+
this.checkpoints.set(this.checkpoints().filter((item) => item.id !== id));
|
|
213
|
+
return true;
|
|
187
214
|
}
|
|
188
|
-
catch {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
195
|
-
routePath(url) {
|
|
196
|
-
return url.split(/[?#]/, 1)[0] || '/';
|
|
197
|
-
}
|
|
198
|
-
nextAutomaticName(route, usedNames = new Set(this.checkpoints().map(({ name }) => name))) {
|
|
199
|
-
if (!usedNames.has(route))
|
|
200
|
-
return route;
|
|
201
|
-
for (let suffix = 2;; suffix += 1) {
|
|
202
|
-
const candidate = `${route} ${suffix}`;
|
|
203
|
-
if (!usedNames.has(candidate))
|
|
204
|
-
return candidate;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
normalizeLegacyNames(checkpoints) {
|
|
208
|
-
const legacyIds = new Set(checkpoints.filter((checkpoint) => this.isLegacyAutomaticName(checkpoint)).map(({ id }) => id));
|
|
209
|
-
if (!legacyIds.size)
|
|
210
|
-
return { checkpoints, changed: false };
|
|
211
|
-
const usedNames = new Set(checkpoints.filter(({ id }) => !legacyIds.has(id)).map(({ name }) => name));
|
|
212
|
-
const names = new Map();
|
|
213
|
-
const oldestFirst = checkpoints
|
|
214
|
-
.filter(({ id }) => legacyIds.has(id))
|
|
215
|
-
.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
|
|
216
|
-
for (const checkpoint of oldestFirst) {
|
|
217
|
-
const name = this.nextAutomaticName(this.routePath(checkpoint.url), usedNames);
|
|
218
|
-
names.set(checkpoint.id, name);
|
|
219
|
-
usedNames.add(name);
|
|
215
|
+
catch (error) {
|
|
216
|
+
this.error.set(this.storageMessage(error, 'The checkpoint could not be deleted.'));
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
finally {
|
|
220
|
+
this.busy.set(false);
|
|
220
221
|
}
|
|
221
|
-
return {
|
|
222
|
-
checkpoints: checkpoints.map((checkpoint) => names.has(checkpoint.id) ? { ...checkpoint, name: names.get(checkpoint.id) } : checkpoint),
|
|
223
|
-
changed: true,
|
|
224
|
-
};
|
|
225
222
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const prefix = `${route} · `;
|
|
229
|
-
if (!checkpoint.name.startsWith(prefix))
|
|
230
|
-
return false;
|
|
231
|
-
const timestamp = checkpoint.name.slice(prefix.length);
|
|
232
|
-
if (timestamp === new Date(checkpoint.createdAt).toLocaleString())
|
|
233
|
-
return true;
|
|
234
|
-
const parsedTimestamp = Date.parse(timestamp);
|
|
235
|
-
const createdTimestamp = Date.parse(checkpoint.createdAt);
|
|
236
|
-
return Number.isFinite(parsedTimestamp) && Number.isFinite(createdTimestamp) &&
|
|
237
|
-
Math.abs(parsedTimestamp - createdTimestamp) < 1000;
|
|
223
|
+
clearError() {
|
|
224
|
+
this.error.set(null);
|
|
238
225
|
}
|
|
239
|
-
|
|
240
|
-
|
|
226
|
+
currentRoute() {
|
|
227
|
+
if (!isPlatformBrowser(this.platformId))
|
|
228
|
+
return '/';
|
|
229
|
+
return `${this.currentPathname()}${window.location.search}${window.location.hash}`;
|
|
241
230
|
}
|
|
242
|
-
|
|
243
|
-
|
|
231
|
+
currentPathname() {
|
|
232
|
+
if (!isPlatformBrowser(this.platformId))
|
|
233
|
+
return '/';
|
|
234
|
+
return window.location.pathname || '/';
|
|
244
235
|
}
|
|
245
|
-
|
|
246
|
-
return
|
|
236
|
+
missingStoreMessage() {
|
|
237
|
+
return 'The NgRx root store is unavailable. Check that provideInspectorCheckpoints() is registered after provideStore().';
|
|
238
|
+
}
|
|
239
|
+
storageMessage(error, fallback) {
|
|
240
|
+
if (isQuotaError(error)) {
|
|
241
|
+
return 'Browser storage is full. Delete old checkpoints, then try saving again.';
|
|
242
|
+
}
|
|
243
|
+
return this.messageFor(error, fallback);
|
|
247
244
|
}
|
|
248
245
|
messageFor(error, fallback) {
|
|
249
|
-
return error instanceof Error ? error.message : fallback;
|
|
246
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
250
247
|
}
|
|
251
|
-
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
252
|
-
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
248
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InspectorCheckpointService, deps: [{ token: INSPECTOR_CHECKPOINT_REPOSITORY }, { token: Store, optional: true }, { token: Router, optional: true }, { token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Injectable }); }
|
|
249
|
+
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InspectorCheckpointService }); }
|
|
253
250
|
}
|
|
254
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type:
|
|
251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InspectorCheckpointService, decorators: [{
|
|
255
252
|
type: Injectable
|
|
256
|
-
}], ctorParameters: () => [{ type:
|
|
253
|
+
}], ctorParameters: () => [{ type: InspectorCheckpointRepository, decorators: [{
|
|
257
254
|
type: Inject,
|
|
258
|
-
args: [
|
|
259
|
-
}] }, { type: i1.
|
|
255
|
+
args: [INSPECTOR_CHECKPOINT_REPOSITORY]
|
|
256
|
+
}] }, { type: i1.Store, decorators: [{
|
|
257
|
+
type: Optional
|
|
258
|
+
}, {
|
|
259
|
+
type: Inject,
|
|
260
|
+
args: [Store]
|
|
261
|
+
}] }, { type: i2.Router, decorators: [{
|
|
262
|
+
type: Optional
|
|
263
|
+
}, {
|
|
260
264
|
type: Inject,
|
|
261
265
|
args: [Router]
|
|
266
|
+
}] }, { type: undefined, decorators: [{
|
|
267
|
+
type: Inject,
|
|
268
|
+
args: [PLATFORM_ID]
|
|
262
269
|
}] }] });
|
|
263
270
|
function provideInspectorCheckpoints() {
|
|
264
|
-
return [
|
|
271
|
+
return makeEnvironmentProviders([
|
|
272
|
+
IndexedDbCheckpointRepository,
|
|
273
|
+
{
|
|
274
|
+
provide: INSPECTOR_CHECKPOINT_REPOSITORY,
|
|
275
|
+
useExisting: IndexedDbCheckpointRepository,
|
|
276
|
+
},
|
|
277
|
+
InspectorCheckpointService,
|
|
278
|
+
{
|
|
279
|
+
provide: META_REDUCERS,
|
|
280
|
+
multi: true,
|
|
281
|
+
useValue: inspectorCheckpointMetaReducer,
|
|
282
|
+
},
|
|
283
|
+
]);
|
|
265
284
|
}
|
|
266
|
-
function
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
285
|
+
function nextAutomaticName(route, names) {
|
|
286
|
+
const used = new Set(names.map((name) => name.toLocaleLowerCase()));
|
|
287
|
+
if (!used.has(route.toLocaleLowerCase()))
|
|
288
|
+
return route;
|
|
289
|
+
for (let suffix = 2;; suffix += 1) {
|
|
290
|
+
const candidate = `${route} ${suffix}`;
|
|
291
|
+
if (!used.has(candidate.toLocaleLowerCase()))
|
|
292
|
+
return candidate;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
function cloneJson(value, failureMessage = 'The NgRx root state is not JSON-serializable. Remove circular or unsupported values and try again.') {
|
|
296
|
+
try {
|
|
297
|
+
const serialized = JSON.stringify(value);
|
|
298
|
+
if (serialized === undefined)
|
|
299
|
+
throw new Error();
|
|
300
|
+
return JSON.parse(serialized);
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
throw new Error(failureMessage);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
function createCheckpointId() {
|
|
307
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
308
|
+
return crypto.randomUUID();
|
|
309
|
+
}
|
|
310
|
+
return `checkpoint-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`;
|
|
311
|
+
}
|
|
312
|
+
function isQuotaError(error) {
|
|
313
|
+
return error instanceof DOMException && (error.name === 'QuotaExceededError' || error.name === 'NS_ERROR_DOM_QUOTA_REACHED');
|
|
314
|
+
}
|
|
315
|
+
function newestFirst$1(a, b) {
|
|
316
|
+
return b.createdAt.localeCompare(a.createdAt) || b.id.localeCompare(a.id);
|
|
317
|
+
}
|
|
318
|
+
function isCheckpointRecord(value) {
|
|
319
|
+
if (!value || typeof value !== 'object')
|
|
320
|
+
return false;
|
|
321
|
+
const checkpoint = value;
|
|
322
|
+
return checkpoint.version === 1 &&
|
|
323
|
+
typeof checkpoint.id === 'string' &&
|
|
324
|
+
typeof checkpoint.name === 'string' &&
|
|
325
|
+
typeof checkpoint.route === 'string' &&
|
|
326
|
+
typeof checkpoint.createdAt === 'string' &&
|
|
327
|
+
'state' in checkpoint;
|
|
272
328
|
}
|
|
273
329
|
|
|
274
330
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
@@ -531,6 +587,56 @@ const getTargetElement = (point, overlayElement) => {
|
|
|
531
587
|
return document.elementFromPoint(point.x, point.y);
|
|
532
588
|
};
|
|
533
589
|
|
|
590
|
+
function fuzzyCheckpoints(checkpoints, query) {
|
|
591
|
+
const normalizedQuery = normalize(query.trim());
|
|
592
|
+
if (!normalizedQuery)
|
|
593
|
+
return [...checkpoints].sort(newestFirst);
|
|
594
|
+
return checkpoints
|
|
595
|
+
.map((checkpoint) => {
|
|
596
|
+
const score = fuzzyScore(checkpoint.name, normalizedQuery);
|
|
597
|
+
return score === null ? null : { checkpoint, score };
|
|
598
|
+
})
|
|
599
|
+
.filter((item) => item !== null)
|
|
600
|
+
.sort((a, b) => b.score - a.score || newestFirst(a.checkpoint, b.checkpoint))
|
|
601
|
+
.map(({ checkpoint }) => checkpoint);
|
|
602
|
+
}
|
|
603
|
+
function fuzzyScore(value, normalizedQuery) {
|
|
604
|
+
const candidate = normalize(value);
|
|
605
|
+
if (candidate === normalizedQuery)
|
|
606
|
+
return 10_000;
|
|
607
|
+
if (candidate.startsWith(normalizedQuery))
|
|
608
|
+
return 8_000 - candidate.length;
|
|
609
|
+
const substringIndex = candidate.indexOf(normalizedQuery);
|
|
610
|
+
if (substringIndex >= 0)
|
|
611
|
+
return 6_000 - substringIndex * 10 - candidate.length;
|
|
612
|
+
let queryIndex = 0;
|
|
613
|
+
let firstIndex = -1;
|
|
614
|
+
let lastIndex = -1;
|
|
615
|
+
let gapPenalty = 0;
|
|
616
|
+
for (let index = 0; index < candidate.length && queryIndex < normalizedQuery.length; index += 1) {
|
|
617
|
+
if (candidate[index] !== normalizedQuery[queryIndex])
|
|
618
|
+
continue;
|
|
619
|
+
if (firstIndex < 0)
|
|
620
|
+
firstIndex = index;
|
|
621
|
+
if (lastIndex >= 0)
|
|
622
|
+
gapPenalty += index - lastIndex - 1;
|
|
623
|
+
lastIndex = index;
|
|
624
|
+
queryIndex += 1;
|
|
625
|
+
}
|
|
626
|
+
if (queryIndex !== normalizedQuery.length)
|
|
627
|
+
return null;
|
|
628
|
+
return 4_000 - firstIndex * 10 - gapPenalty * 20 - candidate.length;
|
|
629
|
+
}
|
|
630
|
+
function normalize(value) {
|
|
631
|
+
return value
|
|
632
|
+
.normalize('NFKD')
|
|
633
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
634
|
+
.toLocaleLowerCase();
|
|
635
|
+
}
|
|
636
|
+
function newestFirst(a, b) {
|
|
637
|
+
return b.createdAt.localeCompare(a.createdAt) || b.id.localeCompare(a.id);
|
|
638
|
+
}
|
|
639
|
+
|
|
534
640
|
const createId = () => typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
535
641
|
? crypto.randomUUID()
|
|
536
642
|
: `${Date.now()}-${Math.random()}`;
|
|
@@ -542,13 +648,22 @@ class inspectorComponent {
|
|
|
542
648
|
this.formatValue = formatValue;
|
|
543
649
|
this.guideHitboxSize = GUIDE_HITBOX_SIZE;
|
|
544
650
|
this.overlayRoot = viewChild('overlayRoot');
|
|
651
|
+
this.checkpointDialog = viewChild('checkpointDialog');
|
|
652
|
+
this.checkpointSearch = viewChild('checkpointSearch');
|
|
545
653
|
this.highlightColor = '#4f8cff';
|
|
546
654
|
this.guideColor = '#ff7a00';
|
|
547
655
|
this.hoverHighlightEnabled = true;
|
|
548
656
|
this.persistOnReload = false;
|
|
549
|
-
this.
|
|
550
|
-
this.
|
|
657
|
+
this.checkpointService = inject(InspectorCheckpointService, { optional: true });
|
|
658
|
+
this.commandBarOpen = signal(false);
|
|
659
|
+
this.checkpointQuery = signal('');
|
|
660
|
+
this.activeCheckpointIndex = signal(0);
|
|
551
661
|
this.editingCheckpointId = signal(null);
|
|
662
|
+
this.deletingCheckpointId = signal(null);
|
|
663
|
+
this.renameDraft = signal('');
|
|
664
|
+
this.checkpointToast = signal(null);
|
|
665
|
+
this.checkpointToastIsError = signal(false);
|
|
666
|
+
this.filteredCheckpoints = computed(() => fuzzyCheckpoints(this.checkpointService?.checkpoints() ?? [], this.checkpointQuery()));
|
|
552
667
|
this.enabled = signal(true);
|
|
553
668
|
this.toolMode = signal('none');
|
|
554
669
|
this.guideOrientation = signal('vertical');
|
|
@@ -584,6 +699,7 @@ class inspectorComponent {
|
|
|
584
699
|
this.draggingGuideId = null;
|
|
585
700
|
this.selectedElement = null;
|
|
586
701
|
this.hoverElement = null;
|
|
702
|
+
this.toastTimer = null;
|
|
587
703
|
this.canUndo = computed(() => this.historyIndex() > 0);
|
|
588
704
|
this.canRedo = computed(() => this.historyIndex() >= 0 &&
|
|
589
705
|
this.historyIndex() < this.history().length - 1);
|
|
@@ -647,6 +763,8 @@ class inspectorComponent {
|
|
|
647
763
|
window.removeEventListener('click', this.handleCapturedClick, {
|
|
648
764
|
capture: true,
|
|
649
765
|
});
|
|
766
|
+
if (this.toastTimer)
|
|
767
|
+
clearTimeout(this.toastTimer);
|
|
650
768
|
}
|
|
651
769
|
setToolMode(mode) {
|
|
652
770
|
if (!this.enabled()) {
|
|
@@ -673,31 +791,114 @@ class inspectorComponent {
|
|
|
673
791
|
this.showTypography.update((value) => !value);
|
|
674
792
|
this.refreshTypographyBlocks();
|
|
675
793
|
}
|
|
676
|
-
toggleCheckpoints() {
|
|
677
|
-
this.checkpointsOpen.update((value) => !value);
|
|
678
|
-
}
|
|
679
794
|
disableInspector() {
|
|
680
795
|
this.setInspectorEnabled(false);
|
|
681
796
|
}
|
|
682
797
|
async saveCheckpoint() {
|
|
683
|
-
await this.
|
|
798
|
+
const checkpoint = await this.checkpointService?.save();
|
|
799
|
+
if (checkpoint) {
|
|
800
|
+
this.showCheckpointToast(`Saved “${checkpoint.name}”.`);
|
|
801
|
+
}
|
|
802
|
+
else if (this.checkpointService?.error()) {
|
|
803
|
+
this.showCheckpointToast(this.checkpointService.error(), true);
|
|
804
|
+
}
|
|
684
805
|
}
|
|
685
|
-
async
|
|
686
|
-
|
|
806
|
+
async openCheckpointCommandBar() {
|
|
807
|
+
if (!this.checkpointService || !this.isBrowser || this.commandBarOpen())
|
|
808
|
+
return;
|
|
809
|
+
this.commandBarOpen.set(true);
|
|
810
|
+
this.checkpointQuery.set('');
|
|
811
|
+
this.activeCheckpointIndex.set(0);
|
|
812
|
+
this.editingCheckpointId.set(null);
|
|
813
|
+
this.deletingCheckpointId.set(null);
|
|
814
|
+
this.checkpointService.clearError();
|
|
815
|
+
this.checkpointDialog()?.nativeElement.showModal();
|
|
816
|
+
queueMicrotask(() => this.checkpointSearch()?.nativeElement.focus());
|
|
817
|
+
await this.checkpointService.load();
|
|
818
|
+
this.clampActiveCheckpoint();
|
|
819
|
+
}
|
|
820
|
+
closeCheckpointCommandBar() {
|
|
821
|
+
if (this.checkpointService?.busy())
|
|
822
|
+
return;
|
|
823
|
+
this.checkpointDialog()?.nativeElement.close();
|
|
824
|
+
this.commandBarOpen.set(false);
|
|
825
|
+
this.editingCheckpointId.set(null);
|
|
826
|
+
this.deletingCheckpointId.set(null);
|
|
687
827
|
}
|
|
688
|
-
|
|
689
|
-
this.
|
|
828
|
+
updateCheckpointQuery(event) {
|
|
829
|
+
this.checkpointQuery.set(event.target.value);
|
|
830
|
+
this.activeCheckpointIndex.set(0);
|
|
831
|
+
this.editingCheckpointId.set(null);
|
|
832
|
+
this.deletingCheckpointId.set(null);
|
|
833
|
+
this.checkpointService?.clearError();
|
|
834
|
+
}
|
|
835
|
+
setActiveCheckpoint(index) {
|
|
836
|
+
this.activeCheckpointIndex.set(index);
|
|
690
837
|
}
|
|
691
|
-
|
|
838
|
+
async restoreCheckpoint(checkpoint) {
|
|
839
|
+
if (!this.checkpointService || this.checkpointService.busy())
|
|
840
|
+
return;
|
|
841
|
+
const restored = await this.checkpointService.restore(checkpoint.id);
|
|
842
|
+
if (!restored)
|
|
843
|
+
return;
|
|
844
|
+
this.checkpointDialog()?.nativeElement.close();
|
|
845
|
+
this.commandBarOpen.set(false);
|
|
846
|
+
this.showCheckpointToast(`Restored “${checkpoint.name}”.`);
|
|
847
|
+
}
|
|
848
|
+
beginRename(checkpoint, event) {
|
|
849
|
+
event?.stopPropagation();
|
|
850
|
+
if (this.checkpointService?.busy())
|
|
851
|
+
return;
|
|
692
852
|
this.editingCheckpointId.set(checkpoint.id);
|
|
853
|
+
this.deletingCheckpointId.set(null);
|
|
854
|
+
this.renameDraft.set(checkpoint.name);
|
|
855
|
+
queueMicrotask(() => {
|
|
856
|
+
const input = this.checkpointDialog()?.nativeElement.querySelector('.inspector-command-row__rename');
|
|
857
|
+
input?.focus();
|
|
858
|
+
input?.select();
|
|
859
|
+
});
|
|
693
860
|
}
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
this.
|
|
697
|
-
|
|
861
|
+
updateRenameDraft(event) {
|
|
862
|
+
this.renameDraft.set(event.target.value);
|
|
863
|
+
this.checkpointService?.clearError();
|
|
864
|
+
}
|
|
865
|
+
async finishRename(checkpoint) {
|
|
866
|
+
if (await this.checkpointService?.rename(checkpoint.id, this.renameDraft())) {
|
|
867
|
+
this.editingCheckpointId.set(null);
|
|
868
|
+
this.checkpointSearch()?.nativeElement.focus();
|
|
869
|
+
}
|
|
698
870
|
}
|
|
699
871
|
cancelRename() {
|
|
700
872
|
this.editingCheckpointId.set(null);
|
|
873
|
+
this.checkpointService?.clearError();
|
|
874
|
+
this.checkpointSearch()?.nativeElement.focus();
|
|
875
|
+
}
|
|
876
|
+
beginDelete(checkpoint, event) {
|
|
877
|
+
event?.stopPropagation();
|
|
878
|
+
if (this.checkpointService?.busy())
|
|
879
|
+
return;
|
|
880
|
+
this.deletingCheckpointId.set(checkpoint.id);
|
|
881
|
+
this.editingCheckpointId.set(null);
|
|
882
|
+
}
|
|
883
|
+
cancelDelete(event) {
|
|
884
|
+
event?.stopPropagation();
|
|
885
|
+
this.deletingCheckpointId.set(null);
|
|
886
|
+
this.checkpointSearch()?.nativeElement.focus();
|
|
887
|
+
}
|
|
888
|
+
async confirmDelete(checkpoint, event) {
|
|
889
|
+
event?.stopPropagation();
|
|
890
|
+
if (!await this.checkpointService?.delete(checkpoint.id))
|
|
891
|
+
return;
|
|
892
|
+
this.deletingCheckpointId.set(null);
|
|
893
|
+
this.clampActiveCheckpoint();
|
|
894
|
+
this.checkpointSearch()?.nativeElement.focus();
|
|
895
|
+
}
|
|
896
|
+
handleCheckpointDialogClick(event) {
|
|
897
|
+
if (event.target === this.checkpointDialog()?.nativeElement &&
|
|
898
|
+
!this.checkpointService?.busy() &&
|
|
899
|
+
!this.deletingCheckpointId()) {
|
|
900
|
+
this.closeCheckpointCommandBar();
|
|
901
|
+
}
|
|
701
902
|
}
|
|
702
903
|
clearGuides() {
|
|
703
904
|
this.guides.set([]);
|
|
@@ -718,6 +919,19 @@ class inspectorComponent {
|
|
|
718
919
|
}
|
|
719
920
|
handleKeydown(event) {
|
|
720
921
|
const key = event.key.toLowerCase();
|
|
922
|
+
if (this.checkpointService &&
|
|
923
|
+
!event.repeat &&
|
|
924
|
+
(event.metaKey || event.ctrlKey) &&
|
|
925
|
+
event.shiftKey &&
|
|
926
|
+
key === 'p') {
|
|
927
|
+
event.preventDefault();
|
|
928
|
+
this.commandBarOpen() ? this.closeCheckpointCommandBar() : void this.openCheckpointCommandBar();
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (this.commandBarOpen()) {
|
|
932
|
+
this.handleCommandBarKeydown(event, key);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
721
935
|
if (key === 'm') {
|
|
722
936
|
const target = event.target;
|
|
723
937
|
if (target?.matches('input, textarea, select') ||
|
|
@@ -890,7 +1104,6 @@ class inspectorComponent {
|
|
|
890
1104
|
this.distanceOverlay.set(null);
|
|
891
1105
|
this.textBlocks.set([]);
|
|
892
1106
|
this.guidePreview.set(null);
|
|
893
|
-
this.checkpointsOpen.set(false);
|
|
894
1107
|
this.altPressed.set(false);
|
|
895
1108
|
this.selectedElement = null;
|
|
896
1109
|
this.hoverElement = null;
|
|
@@ -898,6 +1111,77 @@ class inspectorComponent {
|
|
|
898
1111
|
}
|
|
899
1112
|
this.refreshTypographyBlocks();
|
|
900
1113
|
}
|
|
1114
|
+
handleCommandBarKeydown(event, key) {
|
|
1115
|
+
const checkpoints = this.filteredCheckpoints();
|
|
1116
|
+
const active = checkpoints[this.activeCheckpointIndex()];
|
|
1117
|
+
const target = event.target;
|
|
1118
|
+
const isRenameInput = target?.classList?.contains('inspector-command-row__rename') ?? false;
|
|
1119
|
+
if (key === 'escape') {
|
|
1120
|
+
event.preventDefault();
|
|
1121
|
+
if (this.editingCheckpointId())
|
|
1122
|
+
this.cancelRename();
|
|
1123
|
+
else if (this.deletingCheckpointId())
|
|
1124
|
+
this.cancelDelete();
|
|
1125
|
+
else
|
|
1126
|
+
this.closeCheckpointCommandBar();
|
|
1127
|
+
return;
|
|
1128
|
+
}
|
|
1129
|
+
if (this.checkpointService?.busy())
|
|
1130
|
+
return;
|
|
1131
|
+
const editingCheckpoint = checkpoints.find(({ id }) => id === this.editingCheckpointId());
|
|
1132
|
+
if (isRenameInput && key === 'enter' && editingCheckpoint) {
|
|
1133
|
+
event.preventDefault();
|
|
1134
|
+
void this.finishRename(editingCheckpoint);
|
|
1135
|
+
return;
|
|
1136
|
+
}
|
|
1137
|
+
if (isRenameInput)
|
|
1138
|
+
return;
|
|
1139
|
+
if (key === 'arrowdown') {
|
|
1140
|
+
event.preventDefault();
|
|
1141
|
+
this.activeCheckpointIndex.set(Math.min(this.activeCheckpointIndex() + 1, Math.max(0, checkpoints.length - 1)));
|
|
1142
|
+
this.scrollActiveCheckpointIntoView();
|
|
1143
|
+
return;
|
|
1144
|
+
}
|
|
1145
|
+
if (key === 'arrowup') {
|
|
1146
|
+
event.preventDefault();
|
|
1147
|
+
this.activeCheckpointIndex.set(Math.max(0, this.activeCheckpointIndex() - 1));
|
|
1148
|
+
this.scrollActiveCheckpointIntoView();
|
|
1149
|
+
return;
|
|
1150
|
+
}
|
|
1151
|
+
if (key === 'enter' && active && !this.deletingCheckpointId()) {
|
|
1152
|
+
event.preventDefault();
|
|
1153
|
+
void this.restoreCheckpoint(active);
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (key === 'f2' && active) {
|
|
1157
|
+
event.preventDefault();
|
|
1158
|
+
this.beginRename(active);
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
if (key === 'delete' && active) {
|
|
1162
|
+
event.preventDefault();
|
|
1163
|
+
this.beginDelete(active);
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
clampActiveCheckpoint() {
|
|
1167
|
+
this.activeCheckpointIndex.set(Math.min(this.activeCheckpointIndex(), Math.max(0, this.filteredCheckpoints().length - 1)));
|
|
1168
|
+
}
|
|
1169
|
+
scrollActiveCheckpointIntoView() {
|
|
1170
|
+
queueMicrotask(() => {
|
|
1171
|
+
const row = this.checkpointDialog()?.nativeElement.querySelector(`[data-checkpoint-index="${this.activeCheckpointIndex()}"]`);
|
|
1172
|
+
row?.scrollIntoView({ block: 'nearest' });
|
|
1173
|
+
});
|
|
1174
|
+
}
|
|
1175
|
+
showCheckpointToast(message, isError = false) {
|
|
1176
|
+
if (this.toastTimer)
|
|
1177
|
+
clearTimeout(this.toastTimer);
|
|
1178
|
+
this.checkpointToastIsError.set(isError);
|
|
1179
|
+
this.checkpointToast.set(message);
|
|
1180
|
+
this.toastTimer = setTimeout(() => {
|
|
1181
|
+
this.checkpointToast.set(null);
|
|
1182
|
+
this.checkpointToastIsError.set(false);
|
|
1183
|
+
}, 2500);
|
|
1184
|
+
}
|
|
901
1185
|
selectElementAtPoint(event) {
|
|
902
1186
|
const overlay = this.overlayRoot()?.nativeElement ?? null;
|
|
903
1187
|
const target = getTargetElement({ x: event.clientX, y: event.clientY }, overlay);
|
|
@@ -1042,11 +1326,11 @@ class inspectorComponent {
|
|
|
1042
1326
|
return null;
|
|
1043
1327
|
}
|
|
1044
1328
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: inspectorComponent, deps: [{ token: PLATFORM_ID }], target: i0.ɵɵFactoryTarget.Component }); }
|
|
1045
|
-
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: inspectorComponent, isStandalone: true, selector: "inspector-overlay", inputs: { highlightColor: "highlightColor", guideColor: "guideColor", hoverHighlightEnabled: "hoverHighlightEnabled", persistOnReload: "persistOnReload" }, host: { listeners: { "window:keydown": "handleKeydown($event)", "window:keyup": "handleKeyup($event)", "window:pointermove": "handlePointerMove($event)", "window:pointerup": "handlePointerUp()", "window:click": "handleClick($event)", "window:resize": "handleViewportChange()", "window:scroll": "handleViewportChange()" } }, viewQueries: [{ propertyName: "overlayRoot", first: true, predicate: ["overlayRoot"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"inspector-root\" #overlayRoot>\n @if (enabled()) {\n <div class=\"inspector-toolbar\">\n <div class=\"inspector-toolbar__rail\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'select'\"\n (click)=\"setToolMode('select')\"\n aria-label=\"Select mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2l8 8-3 .7L6.7 13 6 10 3 2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Select</span>\n </button>\n\n @if (checkpointRegistry) {\n <div class=\"inspector-checkpoint-group\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"checkpointsOpen()\"\n (click)=\"toggleCheckpoints()\"\n aria-label=\"Checkpoints\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2.5h10v11H3zm2 2v2h6v-2zm0 4v2h4v-2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Checkpoints</span>\n </button>\n\n @if (checkpointsOpen()) {\n <section class=\"inspector-checkpoint-panel\" aria-label=\"Saved checkpoints\">\n <header class=\"inspector-checkpoint-panel__header\">\n <div>\n <strong>Checkpoints</strong>\n <span>{{ checkpointRegistry.checkpoints().length }}</span>\n </div>\n <button\n type=\"button\"\n class=\"inspector-checkpoint-save\"\n (click)=\"saveCheckpoint()\"\n [disabled]=\"checkpointRegistry.isBusy()\"\n >\n {{ checkpointRegistry.isBusy() ? 'Working\u2026' : 'Save current' }}\n </button>\n </header>\n\n @if (checkpointRegistry.error(); as error) {\n <p class=\"inspector-checkpoint-message inspector-checkpoint-message--error\">{{ error }}</p>\n }\n @if (checkpointRegistry.warning(); as warning) {\n <p class=\"inspector-checkpoint-message\">{{ warning }}</p>\n }\n\n @if (checkpointRegistry.checkpoints().length) {\n <div class=\"inspector-checkpoint-list\">\n @for (checkpoint of checkpointRegistry.checkpoints(); track checkpoint.id) {\n <article class=\"inspector-checkpoint-item\">\n <div class=\"inspector-checkpoint-item__main\">\n @if (editingCheckpointId() === checkpoint.id) {\n <input\n #checkpointName\n class=\"inspector-checkpoint-name-input\"\n [value]=\"checkpoint.name\"\n (keydown.enter)=\"finishRename(checkpoint, $event)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"finishRename(checkpoint, $event)\"\n aria-label=\"Checkpoint name\"\n autofocus\n />\n } @else {\n <button type=\"button\" class=\"inspector-checkpoint-name\" (click)=\"beginRename(checkpoint)\">\n {{ checkpoint.name }}\n </button>\n }\n </div>\n <div class=\"inspector-checkpoint-actions\">\n <button type=\"button\" (click)=\"restoreCheckpoint(checkpoint)\" [disabled]=\"checkpointRegistry.isBusy()\">Restore</button>\n <button type=\"button\" (click)=\"deleteCheckpoint(checkpoint)\" [disabled]=\"checkpointRegistry.isBusy()\">Delete</button>\n </div>\n </article>\n }\n </div>\n } @else {\n <p class=\"inspector-checkpoint-empty\">No checkpoints yet.</p>\n }\n </section>\n }\n </div>\n }\n\n <div class=\"inspector-toolbar__secondary\">\n <div class=\"inspector-toolbar__reveal\">\n <div class=\"inspector-toolbar__reveal-inner\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n aria-label=\"Typography overlay\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Typography</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'vertical'\"\n (click)=\"activateGuideOrientation('vertical')\"\n aria-label=\"Vertical guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 2h1.5v12h-1.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Vertical guides</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'horizontal'\"\n (click)=\"activateGuideOrientation('horizontal')\"\n aria-label=\"Horizontal guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 7.25h12v1.5H2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Horizontal guides</span>\n </button>\n </div>\n </div>\n </div>\n\n <span class=\"inspector-toolbar__divider\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--power\"\n (click)=\"disableInspector()\"\n aria-label=\"Disable inspector; press M to enable\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 1.5h1.5v6h-1.5zM4.1 3.35l.96 1.15A4.25 4.25 0 1 0 10.94 4.5l.96-1.15A5.75 5.75 0 1 1 4.1 3.35z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Disable</span>\n </button>\n </div>\n </div>\n }\n\n @if (enabled() && toolMode() === \"select\") {\n @if (hoverRect() && hoverHighlightEnabled) {\n <div\n class=\"inspector-box inspector-box--hover\"\n [style.left.px]=\"hoverRect()!.left\"\n [style.top.px]=\"hoverRect()!.top\"\n [style.width.px]=\"hoverRect()!.width\"\n [style.height.px]=\"hoverRect()!.height\"\n [style.--inspector-accent]=\"highlightColor\"\n ></div>\n }\n\n @if (selectedMeasurement(); as selected) {\n @if (selected.gap.active && selected.parentRect) {\n <div\n class=\"inspector-box inspector-box--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top\"\n [style.width.px]=\"selected.parentRect.width\"\n [style.height.px]=\"selected.parentRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top - 8\"\n >\n G {{ formatGap(selected) }}\n </div>\n }\n\n <div\n class=\"inspector-box inspector-box--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top\"\n [style.width.px]=\"selected.marginRect.width\"\n [style.height.px]=\"selected.marginRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top - 8\"\n >\n M {{ formatEdges(selected.margin) }}\n </div>\n\n <div\n class=\"inspector-box inspector-box--selected\"\n [style.left.px]=\"selected.rect.left\"\n [style.top.px]=\"selected.rect.top\"\n [style.width.px]=\"selected.rect.width\"\n [style.height.px]=\"selected.rect.height\"\n [style.--inspector-accent]=\"highlightColor\"\n >\n <div class=\"inspector-label\">{{ selectedMetaLine() }}</div>\n </div>\n\n <div\n class=\"inspector-box inspector-box--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"selected.paddingRect.top\"\n [style.width.px]=\"selected.paddingRect.width\"\n [style.height.px]=\"selected.paddingRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"\n selected.paddingRect.top + selected.paddingRect.height + 8\n \"\n >\n P {{ formatEdges(selected.padding) }}\n </div>\n }\n }\n\n @if (enabled() && showTypography()) {\n @for (block of textBlocks(); track block.id) {\n <div\n class=\"inspector-text-chip\"\n [style.left.px]=\"block.rect.left\"\n [style.top.px]=\"block.rect.top - 8\"\n >\n <span\n class=\"inspector-text-chip__swatch\"\n [style.background]=\"block.styles.color\"\n ></span>\n <span>{{ block.styles.fontSize }}</span>\n <span>{{ block.styles.lineHeight }}</span>\n <span>{{ block.styles.color }}</span>\n </div>\n }\n }\n\n @if (enabled() && altPressed() && distanceOverlay(); as distance) {\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectA.left\"\n [style.top.px]=\"distance.rectA.top\"\n [style.width.px]=\"distance.rectA.width\"\n [style.height.px]=\"distance.rectA.height\"\n ></div>\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectB.left\"\n [style.top.px]=\"distance.rectB.top\"\n [style.width.px]=\"distance.rectB.width\"\n [style.height.px]=\"distance.rectB.height\"\n ></div>\n\n @for (\n connector of distance.connectors;\n track trackConnector($index, connector)\n ) {\n <div\n class=\"inspector-connector\"\n [class.inspector-connector--vertical]=\"connector.x1 === connector.x2\"\n [class.inspector-connector--horizontal]=\"connector.y1 === connector.y2\"\n [style.left.px]=\"Math.min(connector.x1, connector.x2)\"\n [style.top.px]=\"Math.min(connector.y1, connector.y2)\"\n [style.width.px]=\"Math.max(1, Math.abs(connector.x2 - connector.x1))\"\n [style.height.px]=\"Math.max(1, Math.abs(connector.y2 - connector.y1))\"\n ></div>\n }\n\n @if (distance.horizontal && distance.horizontal.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--horizontal\"\n [style.left.px]=\"\n Math.min(distance.horizontal.x1!, distance.horizontal.x2!)\n \"\n [style.top.px]=\"distance.horizontal.y!\"\n [style.width.px]=\"\n Math.abs(distance.horizontal.x2! - distance.horizontal.x1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag\"\n [style.left.px]=\"\n (distance.horizontal.x1! + distance.horizontal.x2!) / 2\n \"\n [style.top.px]=\"distance.horizontal.y! + 10\"\n >\n {{ formatValue(distance.horizontal.value) }}\n </div>\n }\n\n @if (distance.vertical && distance.vertical.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--vertical\"\n [style.left.px]=\"distance.vertical.x!\"\n [style.top.px]=\"Math.min(distance.vertical.y1!, distance.vertical.y2!)\"\n [style.height.px]=\"\n Math.abs(distance.vertical.y2! - distance.vertical.y1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag inspector-distance-tag--vertical\"\n [style.left.px]=\"distance.vertical.x! + 10\"\n [style.top.px]=\"(distance.vertical.y1! + distance.vertical.y2!) / 2\"\n >\n {{ formatValue(distance.vertical.value) }}\n </div>\n }\n }\n\n @if (enabled()) {\n @if (toolMode() === \"guides\") {\n @if (guidePreview(); as preview) {\n <div\n class=\"inspector-guide inspector-guide--preview\"\n [class.inspector-guide--vertical]=\"preview.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"\n preview.orientation === 'horizontal'\n \"\n [style.left.px]=\"\n preview.orientation === 'vertical' ? preview.position : null\n \"\n [style.top.px]=\"\n preview.orientation === 'horizontal' ? preview.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n >\n <span class=\"inspector-guide-line\"></span>\n </div>\n }\n }\n\n @for (guide of guides(); track guide.id) {\n <button\n type=\"button\"\n class=\"inspector-guide\"\n [class.inspector-guide--vertical]=\"guide.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"guide.orientation === 'horizontal'\"\n [class.inspector-guide--selected]=\"selectedGuideId() === guide.id\"\n [style.left.px]=\"\n guide.orientation === 'vertical' ? guide.position : null\n \"\n [style.top.px]=\"\n guide.orientation === 'horizontal' ? guide.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n (pointerdown)=\"startGuideDrag($event, guide)\"\n >\n <span class=\"inspector-guide-line\"></span>\n </button>\n }\n }\n</div>\n", styles: [":host{--inspector-panel: rgba(27, 29, 36, .94);--inspector-panel-edge: rgba(255, 255, 255, .08);--inspector-text: #f3f4f6;--inspector-muted: #9ca3af;--inspector-blue: #4f8cff;--inspector-orange: #f59e0b;--inspector-green: #22c55e;--inspector-violet: #a855f7;position:fixed;inset:0;z-index:2147;pointer-events:none;font-family:SF Pro Display,Segoe UI,sans-serif}.inspector-root{position:fixed;inset:0;pointer-events:none}.inspector-toolbar{position:fixed;top:12px;right:12px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail{display:flex;flex-direction:column;align-items:center;gap:2px;width:30px;padding:2px;border-radius:4px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.inspector-toolbar__divider{width:16px;height:1px;margin-block:2px;background:var(--inspector-panel-edge)}.inspector-toolbar__secondary{display:grid;width:24px}.inspector-toolbar__reveal{display:grid;grid-template-rows:0fr;opacity:0;transform:translateY(-4px);transition:grid-template-rows .18s cubic-bezier(.16,1,.3,1),opacity .12s cubic-bezier(.16,1,.3,1),transform .18s cubic-bezier(.16,1,.3,1)}.inspector-toolbar__reveal-inner{display:grid;gap:2px;min-height:0;overflow:hidden}.inspector-toolbar__rail:hover .inspector-toolbar__reveal,.inspector-toolbar__rail:focus-within .inspector-toolbar__reveal{grid-template-rows:1fr;opacity:1;transform:translateY(2px)}.inspector-checkpoint-group{position:relative;width:24px;height:24px}.inspector-checkpoint-panel{position:absolute;top:0;right:calc(100% + 8px);width:min(300px,calc(100vw - 24px));max-height:min(420px,calc(100vh - 64px));overflow:auto;border:1px solid var(--inspector-panel-edge);border-radius:8px;background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;color:var(--inspector-text)}.inspector-checkpoint-panel__header,.inspector-checkpoint-item,.inspector-checkpoint-actions{display:flex;align-items:center}.inspector-checkpoint-panel__header{justify-content:space-between;gap:8px;padding:6px 8px;border-bottom:1px solid var(--inspector-panel-edge)}.inspector-checkpoint-panel__header strong,.inspector-checkpoint-panel__header span{display:inline}.inspector-checkpoint-panel__header strong{font-size:12px}.inspector-checkpoint-panel__header span{margin-left:5px}.inspector-checkpoint-panel__header span,.inspector-checkpoint-empty,.inspector-checkpoint-message{color:var(--inspector-muted);font-size:10px;line-height:1.4}.inspector-checkpoint-save,.inspector-checkpoint-actions button,.inspector-checkpoint-name{border:0;background:transparent;color:inherit;cursor:pointer;font:inherit}.inspector-checkpoint-save{padding:5px 7px;border-radius:5px;background:#4f8cff2e;color:#dbeafe;font-size:10px}.inspector-checkpoint-list{display:grid}.inspector-checkpoint-item{justify-content:space-between;gap:8px;padding:5px 8px;border-bottom:1px solid rgba(255,255,255,.05)}.inspector-checkpoint-item__main{flex:1 1 auto;min-width:0}.inspector-checkpoint-name,.inspector-checkpoint-name-input{display:block;max-width:100%;overflow:hidden;padding:0;color:var(--inspector-text);font-size:11px;font-weight:600;text-align:left;text-overflow:ellipsis;white-space:nowrap}.inspector-checkpoint-name-input{width:100%;border:1px solid rgba(79,140,255,.6);border-radius:3px;background:#0003;outline:none}.inspector-checkpoint-actions{gap:2px;flex:0 0 auto}.inspector-checkpoint-actions button{padding:4px;border-radius:4px;color:var(--inspector-muted);font-size:10px}.inspector-checkpoint-actions button:hover,.inspector-checkpoint-name:hover,.inspector-checkpoint-save:hover{color:var(--inspector-text);background:#ffffff14}.inspector-checkpoint-empty,.inspector-checkpoint-message{margin:0;padding:12px 10px}.inspector-checkpoint-message--error{color:#fecaca}.inspector-icon-button{display:inline-flex;align-items:center;justify-content:center;height:24px;border:0;border-radius:3px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .1s cubic-bezier(.16,1,.3,1),color .1s cubic-bezier(.16,1,.3,1),transform .1s cubic-bezier(.16,1,.3,1);font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{position:relative;width:24px}.inspector-icon-button svg{width:13px;height:13px;fill:currentColor}.inspector-icon-button:active{transform:translateY(1px)}.inspector-icon-button:focus-visible{outline:2px solid var(--inspector-blue);outline-offset:2px}.inspector-icon-button:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--power:hover{background:#ef444429;color:#fecaca}.inspector-tooltip{position:absolute;top:50%;right:calc(100% + 7px);z-index:20;width:max-content;padding:4px 6px;border:1px solid var(--inspector-panel-edge);border-radius:3px;background:#111318fa;color:var(--inspector-text);font-size:10px;font-weight:600;line-height:1.2;opacity:0;pointer-events:none;transform:translate(3px,-50%);transition:opacity .12s cubic-bezier(.16,1,.3,1),transform .12s cubic-bezier(.16,1,.3,1);white-space:nowrap}.inspector-icon-button:hover .inspector-tooltip,.inspector-icon-button:focus-visible .inspector-tooltip{opacity:1;transform:translateY(-50%)}@media (prefers-reduced-motion: reduce){.inspector-toolbar__reveal,.inspector-icon-button,.inspector-tooltip{transition-duration:.01ms}}.inspector-box{position:fixed;box-sizing:border-box;pointer-events:none;border:1px solid var(--inspector-accent, var(--inspector-blue));background:color-mix(in srgb,var(--inspector-accent, var(--inspector-blue)) 8%,transparent)}.inspector-box--hover{opacity:.55}.inspector-box--selected{border-width:1.5px}.inspector-box--margin{border-color:#f59e0bd1;background:#f59e0b14}.inspector-box--padding{border-color:#22c55ed1;background:#22c55e14}.inspector-box--gap{border-color:#a855f7b8;background:#a855f70a}.inspector-box--distance{border-style:dashed;border-color:#4f8cffb3;background:transparent}.inspector-label,.inspector-box-tag,.inspector-text-chip,.inspector-distance-tag{font-variant-numeric:tabular-nums}.inspector-label{position:absolute;left:0;top:0;transform:translateY(calc(-100% - 8px));max-width:min(420px,calc(100vw - 24px));padding:6px 9px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);color:var(--inspector-text);box-shadow:0 18px 40px #00000042;font-size:11px;line-height:1;white-space:nowrap}.inspector-box-tag{position:fixed;transform:translateY(-100%);padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:var(--inspector-text);box-shadow:0 12px 24px #0003;font-size:10px;line-height:1;pointer-events:none}.inspector-box-tag--margin{color:#fbbf24}.inspector-box-tag--padding{color:#86efac;transform:translateY(0)}.inspector-box-tag--gap{color:#d8b4fe}.inspector-text-chip{position:fixed;transform:translateY(-100%);display:inline-flex;gap:6px;align-items:center;padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:#e5e7eb;box-shadow:0 12px 24px #00000038;font-size:10px;line-height:1;white-space:nowrap;pointer-events:none}.inspector-text-chip__swatch{width:8px;height:8px;border-radius:999px;border:1px solid rgba(255,255,255,.18);flex:0 0 auto}.inspector-connector{position:fixed;border-color:#4f8cffb8;border-style:dashed;pointer-events:none}.inspector-connector--vertical{border-left-width:1px;width:0}.inspector-connector--horizontal{border-top-width:1px;height:0}.inspector-distance-line{position:fixed;background:var(--inspector-blue);pointer-events:none}.inspector-distance-line--horizontal{height:1px}.inspector-distance-line--vertical{width:1px}.inspector-distance-tag{position:fixed;transform:translate(-50%);padding:5px 7px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f2;color:var(--inspector-text);font-size:10px;line-height:1;pointer-events:none}.inspector-distance-tag--vertical{transform:translateY(-50%)}.inspector-guide{position:fixed;margin:0;padding:0;border:0;background:transparent;pointer-events:auto;cursor:grab}.inspector-guide--vertical{top:0;width:14px;height:100vh;transform:translate(-50%)}.inspector-guide--horizontal{left:0;width:100vw;height:14px;transform:translateY(-50%)}.inspector-guide--preview{opacity:.55;pointer-events:none;cursor:default}.inspector-guide--preview .inspector-guide-line{background:color-mix(in srgb,var(--inspector-guide, #ff7a00) 72%,transparent)}.inspector-guide-line{position:absolute;inset:0;pointer-events:none}.inspector-guide--vertical .inspector-guide-line{left:calc(50% - .5px);width:1px;height:100%;background:var(--inspector-guide, #ff7a00)}.inspector-guide--horizontal .inspector-guide-line{top:calc(50% - .5px);width:100%;height:1px;background:var(--inspector-guide, #ff7a00)}.inspector-guide--selected .inspector-guide-line{box-shadow:0 0 0 1px #ffffff80}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1329
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: inspectorComponent, isStandalone: true, selector: "inspector-overlay", inputs: { highlightColor: "highlightColor", guideColor: "guideColor", hoverHighlightEnabled: "hoverHighlightEnabled", persistOnReload: "persistOnReload" }, host: { listeners: { "window:keydown": "handleKeydown($event)", "window:keyup": "handleKeyup($event)", "window:pointermove": "handlePointerMove($event)", "window:pointerup": "handlePointerUp()", "window:click": "handleClick($event)", "window:resize": "handleViewportChange()", "window:scroll": "handleViewportChange()" } }, viewQueries: [{ propertyName: "overlayRoot", first: true, predicate: ["overlayRoot"], descendants: true, isSignal: true }, { propertyName: "checkpointDialog", first: true, predicate: ["checkpointDialog"], descendants: true, isSignal: true }, { propertyName: "checkpointSearch", first: true, predicate: ["checkpointSearch"], descendants: true, isSignal: true }], ngImport: i0, template: "<div class=\"inspector-root\" #overlayRoot>\n @if (enabled()) {\n <div class=\"inspector-toolbar\">\n <div class=\"inspector-toolbar__rail\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'select'\"\n (click)=\"setToolMode('select')\"\n aria-label=\"Select mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2l8 8-3 .7L6.7 13 6 10 3 2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Select</span>\n </button>\n\n @if (checkpointService) {\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n (click)=\"saveCheckpoint()\"\n [disabled]=\"checkpointService.busy()\"\n aria-label=\"Save checkpoint\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2.5 2h9.6l1.4 1.4V14h-11zm2 1.5v3h6v-3zm.5 6V12h6V9.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Save checkpoint</span>\n </button>\n }\n\n <div class=\"inspector-toolbar__secondary\">\n <div class=\"inspector-toolbar__reveal\">\n <div class=\"inspector-toolbar__reveal-inner\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n aria-label=\"Typography overlay\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Typography</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'vertical'\"\n (click)=\"activateGuideOrientation('vertical')\"\n aria-label=\"Vertical guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 2h1.5v12h-1.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Vertical guides</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'horizontal'\"\n (click)=\"activateGuideOrientation('horizontal')\"\n aria-label=\"Horizontal guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 7.25h12v1.5H2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Horizontal guides</span>\n </button>\n </div>\n </div>\n </div>\n\n <span class=\"inspector-toolbar__divider\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--power\"\n (click)=\"disableInspector()\"\n aria-label=\"Disable inspector; press M to enable\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 1.5h1.5v6h-1.5zM4.1 3.35l.96 1.15A4.25 4.25 0 1 0 10.94 4.5l.96-1.15A5.75 5.75 0 1 1 4.1 3.35z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Disable</span>\n </button>\n </div>\n </div>\n }\n\n @if (checkpointService) {\n <dialog\n #checkpointDialog\n class=\"inspector-command-dialog\"\n aria-labelledby=\"inspector-command-title\"\n (cancel)=\"$event.preventDefault(); closeCheckpointCommandBar()\"\n (click)=\"handleCheckpointDialogClick($event)\"\n >\n <section class=\"inspector-command\" (click)=\"$event.stopPropagation()\">\n <header class=\"inspector-command__search\">\n <label id=\"inspector-command-title\" class=\"inspector-visually-hidden\" for=\"inspector-checkpoint-search\">\n Search checkpoints\n </label>\n <input\n #checkpointSearch\n id=\"inspector-checkpoint-search\"\n type=\"search\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n placeholder=\"Find a checkpoint\u2026\"\n [value]=\"checkpointQuery()\"\n [attr.aria-controls]=\"'inspector-checkpoint-results'\"\n [attr.aria-activedescendant]=\"filteredCheckpoints().length ? 'inspector-checkpoint-' + filteredCheckpoints()[activeCheckpointIndex()].id : null\"\n (input)=\"updateCheckpointQuery($event)\"\n />\n </header>\n\n @if (checkpointService.error(); as error) {\n <p class=\"inspector-command__message inspector-command__message--error\" role=\"alert\">{{ error }}</p>\n }\n\n <div id=\"inspector-checkpoint-results\" class=\"inspector-command__results\" role=\"listbox\" aria-label=\"Saved checkpoints\">\n @if (checkpointService.loading()) {\n <p class=\"inspector-command__message\" role=\"status\">Loading checkpoints\u2026</p>\n } @else if (!checkpointService.checkpoints().length) {\n <p class=\"inspector-command__message\" role=\"status\">No checkpoints yet. Save one from the Inspector toolbar.</p>\n } @else if (!filteredCheckpoints().length) {\n <p class=\"inspector-command__message\" role=\"status\">No checkpoint matches \u201C{{ checkpointQuery() }}\u201D.</p>\n } @else {\n @for (checkpoint of filteredCheckpoints(); track checkpoint.id; let index = $index) {\n <div\n class=\"inspector-command-row\"\n role=\"option\"\n [id]=\"'inspector-checkpoint-' + checkpoint.id\"\n [attr.data-checkpoint-index]=\"index\"\n [attr.aria-selected]=\"activeCheckpointIndex() === index\"\n [class.is-active]=\"activeCheckpointIndex() === index\"\n [class.is-busy]=\"checkpointService.busy() && activeCheckpointIndex() === index\"\n (mouseenter)=\"setActiveCheckpoint(index)\"\n (click)=\"deletingCheckpointId() === checkpoint.id || editingCheckpointId() === checkpoint.id ? null : restoreCheckpoint(checkpoint)\"\n >\n @if (deletingCheckpointId() === checkpoint.id) {\n <span class=\"inspector-command-row__confirm\">Delete \u201C{{ checkpoint.name }}\u201D?</span>\n <span class=\"inspector-command-row__confirm-actions\">\n <button type=\"button\" class=\"is-danger\" (click)=\"confirmDelete(checkpoint, $event)\">Delete checkpoint</button>\n <button type=\"button\" (click)=\"cancelDelete($event)\">Keep checkpoint</button>\n </span>\n } @else if (editingCheckpointId() === checkpoint.id) {\n <input\n class=\"inspector-command-row__rename\"\n aria-label=\"Checkpoint name\"\n [value]=\"renameDraft()\"\n (input)=\"updateRenameDraft($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span class=\"inspector-command-row__name\">\n @if (checkpointService.busy() && activeCheckpointIndex() === index) {\n Restoring \u201C{{ checkpoint.name }}\u201D\u2026\n } @else {\n {{ checkpoint.name }}\n }\n </span>\n <span class=\"inspector-command-row__actions\">\n <button type=\"button\" (click)=\"beginRename(checkpoint, $event)\" [attr.aria-label]=\"'Rename ' + checkpoint.name\">\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"m11.8 1.8 2.4 2.4-8.3 8.3-3.1.7.7-3.1zm-8 10.4 1.5-.3-1.2-1.2z\" /></svg>\n </button>\n <button type=\"button\" (click)=\"beginDelete(checkpoint, $event)\" [attr.aria-label]=\"'Delete ' + checkpoint.name\">\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M5.5 2h5l.5 1H14v1.25H2V3h3zm-2 3.25h9L12 14H4z\" /></svg>\n </button>\n </span>\n }\n </div>\n }\n }\n </div>\n </section>\n </dialog>\n }\n\n @if (checkpointToast(); as toast) {\n <p\n class=\"inspector-checkpoint-toast\"\n [class.inspector-checkpoint-toast--error]=\"checkpointToastIsError()\"\n [attr.role]=\"checkpointToastIsError() ? 'alert' : 'status'\"\n >{{ toast }}</p>\n }\n\n @if (enabled() && toolMode() === \"select\") {\n @if (hoverRect() && hoverHighlightEnabled) {\n <div\n class=\"inspector-box inspector-box--hover\"\n [style.left.px]=\"hoverRect()!.left\"\n [style.top.px]=\"hoverRect()!.top\"\n [style.width.px]=\"hoverRect()!.width\"\n [style.height.px]=\"hoverRect()!.height\"\n [style.--inspector-accent]=\"highlightColor\"\n ></div>\n }\n\n @if (selectedMeasurement(); as selected) {\n @if (selected.gap.active && selected.parentRect) {\n <div\n class=\"inspector-box inspector-box--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top\"\n [style.width.px]=\"selected.parentRect.width\"\n [style.height.px]=\"selected.parentRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top - 8\"\n >\n G {{ formatGap(selected) }}\n </div>\n }\n\n <div\n class=\"inspector-box inspector-box--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top\"\n [style.width.px]=\"selected.marginRect.width\"\n [style.height.px]=\"selected.marginRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top - 8\"\n >\n M {{ formatEdges(selected.margin) }}\n </div>\n\n <div\n class=\"inspector-box inspector-box--selected\"\n [style.left.px]=\"selected.rect.left\"\n [style.top.px]=\"selected.rect.top\"\n [style.width.px]=\"selected.rect.width\"\n [style.height.px]=\"selected.rect.height\"\n [style.--inspector-accent]=\"highlightColor\"\n >\n <div class=\"inspector-label\">{{ selectedMetaLine() }}</div>\n </div>\n\n <div\n class=\"inspector-box inspector-box--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"selected.paddingRect.top\"\n [style.width.px]=\"selected.paddingRect.width\"\n [style.height.px]=\"selected.paddingRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"\n selected.paddingRect.top + selected.paddingRect.height + 8\n \"\n >\n P {{ formatEdges(selected.padding) }}\n </div>\n }\n }\n\n @if (enabled() && showTypography()) {\n @for (block of textBlocks(); track block.id) {\n <div\n class=\"inspector-text-chip\"\n [style.left.px]=\"block.rect.left\"\n [style.top.px]=\"block.rect.top - 8\"\n >\n <span\n class=\"inspector-text-chip__swatch\"\n [style.background]=\"block.styles.color\"\n ></span>\n <span>{{ block.styles.fontSize }}</span>\n <span>{{ block.styles.lineHeight }}</span>\n <span>{{ block.styles.color }}</span>\n </div>\n }\n }\n\n @if (enabled() && altPressed() && distanceOverlay(); as distance) {\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectA.left\"\n [style.top.px]=\"distance.rectA.top\"\n [style.width.px]=\"distance.rectA.width\"\n [style.height.px]=\"distance.rectA.height\"\n ></div>\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectB.left\"\n [style.top.px]=\"distance.rectB.top\"\n [style.width.px]=\"distance.rectB.width\"\n [style.height.px]=\"distance.rectB.height\"\n ></div>\n\n @for (\n connector of distance.connectors;\n track trackConnector($index, connector)\n ) {\n <div\n class=\"inspector-connector\"\n [class.inspector-connector--vertical]=\"connector.x1 === connector.x2\"\n [class.inspector-connector--horizontal]=\"connector.y1 === connector.y2\"\n [style.left.px]=\"Math.min(connector.x1, connector.x2)\"\n [style.top.px]=\"Math.min(connector.y1, connector.y2)\"\n [style.width.px]=\"Math.max(1, Math.abs(connector.x2 - connector.x1))\"\n [style.height.px]=\"Math.max(1, Math.abs(connector.y2 - connector.y1))\"\n ></div>\n }\n\n @if (distance.horizontal && distance.horizontal.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--horizontal\"\n [style.left.px]=\"\n Math.min(distance.horizontal.x1!, distance.horizontal.x2!)\n \"\n [style.top.px]=\"distance.horizontal.y!\"\n [style.width.px]=\"\n Math.abs(distance.horizontal.x2! - distance.horizontal.x1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag\"\n [style.left.px]=\"\n (distance.horizontal.x1! + distance.horizontal.x2!) / 2\n \"\n [style.top.px]=\"distance.horizontal.y! + 10\"\n >\n {{ formatValue(distance.horizontal.value) }}\n </div>\n }\n\n @if (distance.vertical && distance.vertical.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--vertical\"\n [style.left.px]=\"distance.vertical.x!\"\n [style.top.px]=\"Math.min(distance.vertical.y1!, distance.vertical.y2!)\"\n [style.height.px]=\"\n Math.abs(distance.vertical.y2! - distance.vertical.y1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag inspector-distance-tag--vertical\"\n [style.left.px]=\"distance.vertical.x! + 10\"\n [style.top.px]=\"(distance.vertical.y1! + distance.vertical.y2!) / 2\"\n >\n {{ formatValue(distance.vertical.value) }}\n </div>\n }\n }\n\n @if (enabled()) {\n @if (toolMode() === \"guides\") {\n @if (guidePreview(); as preview) {\n <div\n class=\"inspector-guide inspector-guide--preview\"\n [class.inspector-guide--vertical]=\"preview.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"\n preview.orientation === 'horizontal'\n \"\n [style.left.px]=\"\n preview.orientation === 'vertical' ? preview.position : null\n \"\n [style.top.px]=\"\n preview.orientation === 'horizontal' ? preview.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n >\n <span class=\"inspector-guide-line\"></span>\n </div>\n }\n }\n\n @for (guide of guides(); track guide.id) {\n <button\n type=\"button\"\n class=\"inspector-guide\"\n [class.inspector-guide--vertical]=\"guide.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"guide.orientation === 'horizontal'\"\n [class.inspector-guide--selected]=\"selectedGuideId() === guide.id\"\n [style.left.px]=\"\n guide.orientation === 'vertical' ? guide.position : null\n \"\n [style.top.px]=\"\n guide.orientation === 'horizontal' ? guide.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n (pointerdown)=\"startGuideDrag($event, guide)\"\n >\n <span class=\"inspector-guide-line\"></span>\n </button>\n }\n }\n</div>\n", styles: [":host{--inspector-panel: rgba(27, 29, 36, .94);--inspector-panel-edge: rgba(255, 255, 255, .08);--inspector-text: #f3f4f6;--inspector-muted: #9ca3af;--inspector-blue: #4f8cff;--inspector-orange: #f59e0b;--inspector-green: #22c55e;--inspector-violet: #a855f7;position:fixed;inset:0;z-index:2147;pointer-events:none;font-family:SF Pro Display,Segoe UI,sans-serif}.inspector-root{position:fixed;inset:0;pointer-events:none}.inspector-toolbar{position:fixed;top:12px;right:12px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail{display:flex;flex-direction:column;align-items:center;gap:2px;width:30px;padding:2px;border-radius:4px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.inspector-toolbar__divider{width:16px;height:1px;margin-block:2px;background:var(--inspector-panel-edge)}.inspector-toolbar__secondary{display:grid;width:24px}.inspector-toolbar__reveal{display:grid;grid-template-rows:0fr;opacity:0;transform:translateY(-4px);transition:grid-template-rows .18s cubic-bezier(.16,1,.3,1),opacity .12s cubic-bezier(.16,1,.3,1),transform .18s cubic-bezier(.16,1,.3,1)}.inspector-toolbar__reveal-inner{display:grid;gap:2px;min-height:0;overflow:hidden}.inspector-toolbar__rail:hover .inspector-toolbar__reveal,.inspector-toolbar__rail:focus-within .inspector-toolbar__reveal{grid-template-rows:1fr;opacity:1;transform:translateY(2px)}.inspector-command-dialog{--command-surface: oklch(20% .012 270);--command-surface-raised: oklch(25% .014 270);--command-surface-active: oklch(27% .018 270);--command-edge: oklch(39% .014 270);--command-text: oklch(95% .008 270);--command-muted: oklch(73% .018 270);--command-accent: oklch(76% .08 260);--command-danger: oklch(71% .17 25);box-sizing:border-box;inset-block-start:max(40px,10vh);width:min(480px,calc(100vw - 24px));max-width:none;max-height:min(400px,calc(100vh - 64px));margin:0 auto;padding:0;overflow:hidden;border:1px solid var(--command-edge);border-radius:4px;background:var(--command-surface);color:var(--command-text);box-shadow:0 20px 56px #0102038f;opacity:0;pointer-events:auto;transform:translateY(-6px);transition:opacity .12s cubic-bezier(.7,0,.84,0),transform .12s cubic-bezier(.7,0,.84,0),overlay .12s allow-discrete,display .12s allow-discrete}.inspector-command-dialog[open]{opacity:1;transform:translateY(0);transition-duration:.16s;transition-timing-function:cubic-bezier(.16,1,.3,1)}@starting-style{.inspector-command-dialog[open]{opacity:0;transform:translateY(-6px)}}.inspector-command-dialog::backdrop{background:#0102037a}.inspector-command{display:grid;min-width:0}.inspector-command__search{min-height:44px;padding-inline:12px;border-bottom:1px solid var(--command-edge)}.inspector-command__search input,.inspector-command-row__rename{min-width:0;border:0;outline:0;background:transparent;color:var(--command-text);font:inherit}.inspector-command__search input{width:100%;height:43px;padding:0;font-size:14px;line-height:1.45}.inspector-command__search:has(input:focus-visible){box-shadow:inset 0 -2px var(--command-accent)}.inspector-command__search input::placeholder{color:var(--command-muted);opacity:1}.inspector-command__search input::-webkit-search-cancel-button{display:none}.inspector-command__results{max-height:304px;overflow:auto;padding-block:3px;scrollbar-color:var(--command-edge) transparent}.inspector-command-row{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px;min-height:38px;padding-inline:12px 6px;cursor:pointer;-webkit-user-select:none;user-select:none}.inspector-command-row.is-active{background:var(--command-surface-active)}.inspector-command-row.is-busy{cursor:wait;pointer-events:none}.inspector-command-row__name{overflow:hidden;color:var(--command-text);font-size:12px;font-weight:540;line-height:1.45;text-overflow:ellipsis;white-space:nowrap}.inspector-command-row.is-active .inspector-command-row__name{font-weight:700}.inspector-command-row__actions,.inspector-command-row__confirm-actions{display:flex;align-items:center;gap:4px}.inspector-command-row__actions{opacity:0;pointer-events:none}.inspector-command-row:hover .inspector-command-row__actions,.inspector-command-row:focus-within .inspector-command-row__actions{opacity:1;pointer-events:auto}.inspector-command-row button{display:inline-grid;width:26px;min-height:26px;padding:5px;place-items:center;border:1px solid transparent;border-radius:3px;background:transparent;color:var(--command-muted);cursor:pointer;font-family:inherit;font-size:10px;font-weight:600;line-height:1.3}.inspector-command-row button svg{width:12px;height:12px;fill:currentColor}.inspector-command-row button:hover{border-color:var(--command-edge);background:var(--command-surface-raised);color:var(--command-text)}.inspector-command-row button.is-danger{color:var(--command-danger)}.inspector-command-row button:focus-visible,.inspector-command-row__rename:focus-visible{outline:2px solid var(--command-accent);outline-offset:2px}.inspector-command-row__confirm{grid-column:1;color:var(--command-text);font-size:12px;font-weight:620}.inspector-command-row__rename{grid-column:1 / -1;min-height:30px;padding-inline:8px;border:1px solid var(--command-accent);border-radius:3px;background:var(--command-surface-raised);font-size:12px}.inspector-command__message{margin:0;padding:20px 12px;color:var(--command-muted);font-size:12px;line-height:1.55;text-align:center}.inspector-command__message--error{padding-block:9px;border-bottom:1px solid oklch(45% .1 25);background:#3f1917;color:oklch(89% .06 25);text-align:left}.inspector-visually-hidden{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.inspector-checkpoint-toast{position:fixed;top:12px;left:50%;z-index:500;margin:0;padding:8px 12px;border:1px solid oklch(48% .04 150);border-radius:4px;background:#152619;color:#cfe9d4;box-shadow:0 16px 40px #01020375;font-size:11px;font-weight:620;line-height:1.35;pointer-events:none;transform:translate(-50%)}.inspector-checkpoint-toast--error{border-color:#9c433f;background:#431614;color:oklch(92% .05 25)}.inspector-icon-button{display:inline-flex;align-items:center;justify-content:center;height:24px;border:0;border-radius:3px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .1s cubic-bezier(.16,1,.3,1),color .1s cubic-bezier(.16,1,.3,1),transform .1s cubic-bezier(.16,1,.3,1);font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{position:relative;width:24px}.inspector-icon-button svg{width:13px;height:13px;fill:currentColor}.inspector-icon-button:active{transform:translateY(1px)}.inspector-icon-button:focus-visible{outline:2px solid var(--inspector-blue);outline-offset:2px}.inspector-icon-button:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--power:hover{background:#ef444429;color:#fecaca}.inspector-tooltip{position:absolute;top:50%;right:calc(100% + 7px);z-index:20;width:max-content;padding:4px 6px;border:1px solid var(--inspector-panel-edge);border-radius:3px;background:#111318fa;color:var(--inspector-text);font-size:10px;font-weight:600;line-height:1.2;opacity:0;pointer-events:none;transform:translate(3px,-50%);transition:opacity .12s cubic-bezier(.16,1,.3,1),transform .12s cubic-bezier(.16,1,.3,1);white-space:nowrap}.inspector-icon-button:hover .inspector-tooltip,.inspector-icon-button:focus-visible .inspector-tooltip{opacity:1;transform:translateY(-50%)}@media (prefers-reduced-motion: reduce){.inspector-toolbar__reveal,.inspector-icon-button,.inspector-tooltip,.inspector-command-dialog,.inspector-command-row__marker{transition-duration:.01ms}.inspector-command-dialog,.inspector-command-dialog[open]{transform:none}}.inspector-box{position:fixed;box-sizing:border-box;pointer-events:none;border:1px solid var(--inspector-accent, var(--inspector-blue));background:color-mix(in srgb,var(--inspector-accent, var(--inspector-blue)) 8%,transparent)}.inspector-box--hover{opacity:.55}.inspector-box--selected{border-width:1.5px}.inspector-box--margin{border-color:#f59e0bd1;background:#f59e0b14}.inspector-box--padding{border-color:#22c55ed1;background:#22c55e14}.inspector-box--gap{border-color:#a855f7b8;background:#a855f70a}.inspector-box--distance{border-style:dashed;border-color:#4f8cffb3;background:transparent}.inspector-label,.inspector-box-tag,.inspector-text-chip,.inspector-distance-tag{font-variant-numeric:tabular-nums}.inspector-label{position:absolute;left:0;top:0;transform:translateY(calc(-100% - 8px));max-width:min(420px,calc(100vw - 24px));padding:6px 9px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);color:var(--inspector-text);box-shadow:0 18px 40px #00000042;font-size:11px;line-height:1;white-space:nowrap}.inspector-box-tag{position:fixed;transform:translateY(-100%);padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:var(--inspector-text);box-shadow:0 12px 24px #0003;font-size:10px;line-height:1;pointer-events:none}.inspector-box-tag--margin{color:#fbbf24}.inspector-box-tag--padding{color:#86efac;transform:translateY(0)}.inspector-box-tag--gap{color:#d8b4fe}.inspector-text-chip{position:fixed;transform:translateY(-100%);display:inline-flex;gap:6px;align-items:center;padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:#e5e7eb;box-shadow:0 12px 24px #00000038;font-size:10px;line-height:1;white-space:nowrap;pointer-events:none}.inspector-text-chip__swatch{width:8px;height:8px;border-radius:999px;border:1px solid rgba(255,255,255,.18);flex:0 0 auto}.inspector-connector{position:fixed;border-color:#4f8cffb8;border-style:dashed;pointer-events:none}.inspector-connector--vertical{border-left-width:1px;width:0}.inspector-connector--horizontal{border-top-width:1px;height:0}.inspector-distance-line{position:fixed;background:var(--inspector-blue);pointer-events:none}.inspector-distance-line--horizontal{height:1px}.inspector-distance-line--vertical{width:1px}.inspector-distance-tag{position:fixed;transform:translate(-50%);padding:5px 7px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f2;color:var(--inspector-text);font-size:10px;line-height:1;pointer-events:none}.inspector-distance-tag--vertical{transform:translateY(-50%)}.inspector-guide{position:fixed;margin:0;padding:0;border:0;background:transparent;pointer-events:auto;cursor:grab}.inspector-guide--vertical{top:0;width:14px;height:100vh;transform:translate(-50%)}.inspector-guide--horizontal{left:0;width:100vw;height:14px;transform:translateY(-50%)}.inspector-guide--preview{opacity:.55;pointer-events:none;cursor:default}.inspector-guide--preview .inspector-guide-line{background:color-mix(in srgb,var(--inspector-guide, #ff7a00) 72%,transparent)}.inspector-guide-line{position:absolute;inset:0;pointer-events:none}.inspector-guide--vertical .inspector-guide-line{left:calc(50% - .5px);width:1px;height:100%;background:var(--inspector-guide, #ff7a00)}.inspector-guide--horizontal .inspector-guide-line{top:calc(50% - .5px);width:100%;height:1px;background:var(--inspector-guide, #ff7a00)}.inspector-guide--selected .inspector-guide-line{box-shadow:0 0 0 1px #ffffff80}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
1046
1330
|
}
|
|
1047
1331
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: inspectorComponent, decorators: [{
|
|
1048
1332
|
type: Component,
|
|
1049
|
-
args: [{ selector: 'inspector-overlay', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"inspector-root\" #overlayRoot>\n @if (enabled()) {\n <div class=\"inspector-toolbar\">\n <div class=\"inspector-toolbar__rail\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'select'\"\n (click)=\"setToolMode('select')\"\n aria-label=\"Select mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2l8 8-3 .7L6.7 13 6 10 3 2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Select</span>\n </button>\n\n @if (checkpointRegistry) {\n <div class=\"inspector-checkpoint-group\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"checkpointsOpen()\"\n (click)=\"toggleCheckpoints()\"\n aria-label=\"Checkpoints\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2.5h10v11H3zm2 2v2h6v-2zm0 4v2h4v-2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Checkpoints</span>\n </button>\n\n @if (checkpointsOpen()) {\n <section class=\"inspector-checkpoint-panel\" aria-label=\"Saved checkpoints\">\n <header class=\"inspector-checkpoint-panel__header\">\n <div>\n <strong>Checkpoints</strong>\n <span>{{ checkpointRegistry.checkpoints().length }}</span>\n </div>\n <button\n type=\"button\"\n class=\"inspector-checkpoint-save\"\n (click)=\"saveCheckpoint()\"\n [disabled]=\"checkpointRegistry.isBusy()\"\n >\n {{ checkpointRegistry.isBusy() ? 'Working\u2026' : 'Save current' }}\n </button>\n </header>\n\n @if (checkpointRegistry.error(); as error) {\n <p class=\"inspector-checkpoint-message inspector-checkpoint-message--error\">{{ error }}</p>\n }\n @if (checkpointRegistry.warning(); as warning) {\n <p class=\"inspector-checkpoint-message\">{{ warning }}</p>\n }\n\n @if (checkpointRegistry.checkpoints().length) {\n <div class=\"inspector-checkpoint-list\">\n @for (checkpoint of checkpointRegistry.checkpoints(); track checkpoint.id) {\n <article class=\"inspector-checkpoint-item\">\n <div class=\"inspector-checkpoint-item__main\">\n @if (editingCheckpointId() === checkpoint.id) {\n <input\n #checkpointName\n class=\"inspector-checkpoint-name-input\"\n [value]=\"checkpoint.name\"\n (keydown.enter)=\"finishRename(checkpoint, $event)\"\n (keydown.escape)=\"cancelRename()\"\n (blur)=\"finishRename(checkpoint, $event)\"\n aria-label=\"Checkpoint name\"\n autofocus\n />\n } @else {\n <button type=\"button\" class=\"inspector-checkpoint-name\" (click)=\"beginRename(checkpoint)\">\n {{ checkpoint.name }}\n </button>\n }\n </div>\n <div class=\"inspector-checkpoint-actions\">\n <button type=\"button\" (click)=\"restoreCheckpoint(checkpoint)\" [disabled]=\"checkpointRegistry.isBusy()\">Restore</button>\n <button type=\"button\" (click)=\"deleteCheckpoint(checkpoint)\" [disabled]=\"checkpointRegistry.isBusy()\">Delete</button>\n </div>\n </article>\n }\n </div>\n } @else {\n <p class=\"inspector-checkpoint-empty\">No checkpoints yet.</p>\n }\n </section>\n }\n </div>\n }\n\n <div class=\"inspector-toolbar__secondary\">\n <div class=\"inspector-toolbar__reveal\">\n <div class=\"inspector-toolbar__reveal-inner\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n aria-label=\"Typography overlay\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Typography</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'vertical'\"\n (click)=\"activateGuideOrientation('vertical')\"\n aria-label=\"Vertical guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 2h1.5v12h-1.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Vertical guides</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'horizontal'\"\n (click)=\"activateGuideOrientation('horizontal')\"\n aria-label=\"Horizontal guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 7.25h12v1.5H2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Horizontal guides</span>\n </button>\n </div>\n </div>\n </div>\n\n <span class=\"inspector-toolbar__divider\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--power\"\n (click)=\"disableInspector()\"\n aria-label=\"Disable inspector; press M to enable\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 1.5h1.5v6h-1.5zM4.1 3.35l.96 1.15A4.25 4.25 0 1 0 10.94 4.5l.96-1.15A5.75 5.75 0 1 1 4.1 3.35z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Disable</span>\n </button>\n </div>\n </div>\n }\n\n @if (enabled() && toolMode() === \"select\") {\n @if (hoverRect() && hoverHighlightEnabled) {\n <div\n class=\"inspector-box inspector-box--hover\"\n [style.left.px]=\"hoverRect()!.left\"\n [style.top.px]=\"hoverRect()!.top\"\n [style.width.px]=\"hoverRect()!.width\"\n [style.height.px]=\"hoverRect()!.height\"\n [style.--inspector-accent]=\"highlightColor\"\n ></div>\n }\n\n @if (selectedMeasurement(); as selected) {\n @if (selected.gap.active && selected.parentRect) {\n <div\n class=\"inspector-box inspector-box--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top\"\n [style.width.px]=\"selected.parentRect.width\"\n [style.height.px]=\"selected.parentRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top - 8\"\n >\n G {{ formatGap(selected) }}\n </div>\n }\n\n <div\n class=\"inspector-box inspector-box--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top\"\n [style.width.px]=\"selected.marginRect.width\"\n [style.height.px]=\"selected.marginRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top - 8\"\n >\n M {{ formatEdges(selected.margin) }}\n </div>\n\n <div\n class=\"inspector-box inspector-box--selected\"\n [style.left.px]=\"selected.rect.left\"\n [style.top.px]=\"selected.rect.top\"\n [style.width.px]=\"selected.rect.width\"\n [style.height.px]=\"selected.rect.height\"\n [style.--inspector-accent]=\"highlightColor\"\n >\n <div class=\"inspector-label\">{{ selectedMetaLine() }}</div>\n </div>\n\n <div\n class=\"inspector-box inspector-box--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"selected.paddingRect.top\"\n [style.width.px]=\"selected.paddingRect.width\"\n [style.height.px]=\"selected.paddingRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"\n selected.paddingRect.top + selected.paddingRect.height + 8\n \"\n >\n P {{ formatEdges(selected.padding) }}\n </div>\n }\n }\n\n @if (enabled() && showTypography()) {\n @for (block of textBlocks(); track block.id) {\n <div\n class=\"inspector-text-chip\"\n [style.left.px]=\"block.rect.left\"\n [style.top.px]=\"block.rect.top - 8\"\n >\n <span\n class=\"inspector-text-chip__swatch\"\n [style.background]=\"block.styles.color\"\n ></span>\n <span>{{ block.styles.fontSize }}</span>\n <span>{{ block.styles.lineHeight }}</span>\n <span>{{ block.styles.color }}</span>\n </div>\n }\n }\n\n @if (enabled() && altPressed() && distanceOverlay(); as distance) {\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectA.left\"\n [style.top.px]=\"distance.rectA.top\"\n [style.width.px]=\"distance.rectA.width\"\n [style.height.px]=\"distance.rectA.height\"\n ></div>\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectB.left\"\n [style.top.px]=\"distance.rectB.top\"\n [style.width.px]=\"distance.rectB.width\"\n [style.height.px]=\"distance.rectB.height\"\n ></div>\n\n @for (\n connector of distance.connectors;\n track trackConnector($index, connector)\n ) {\n <div\n class=\"inspector-connector\"\n [class.inspector-connector--vertical]=\"connector.x1 === connector.x2\"\n [class.inspector-connector--horizontal]=\"connector.y1 === connector.y2\"\n [style.left.px]=\"Math.min(connector.x1, connector.x2)\"\n [style.top.px]=\"Math.min(connector.y1, connector.y2)\"\n [style.width.px]=\"Math.max(1, Math.abs(connector.x2 - connector.x1))\"\n [style.height.px]=\"Math.max(1, Math.abs(connector.y2 - connector.y1))\"\n ></div>\n }\n\n @if (distance.horizontal && distance.horizontal.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--horizontal\"\n [style.left.px]=\"\n Math.min(distance.horizontal.x1!, distance.horizontal.x2!)\n \"\n [style.top.px]=\"distance.horizontal.y!\"\n [style.width.px]=\"\n Math.abs(distance.horizontal.x2! - distance.horizontal.x1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag\"\n [style.left.px]=\"\n (distance.horizontal.x1! + distance.horizontal.x2!) / 2\n \"\n [style.top.px]=\"distance.horizontal.y! + 10\"\n >\n {{ formatValue(distance.horizontal.value) }}\n </div>\n }\n\n @if (distance.vertical && distance.vertical.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--vertical\"\n [style.left.px]=\"distance.vertical.x!\"\n [style.top.px]=\"Math.min(distance.vertical.y1!, distance.vertical.y2!)\"\n [style.height.px]=\"\n Math.abs(distance.vertical.y2! - distance.vertical.y1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag inspector-distance-tag--vertical\"\n [style.left.px]=\"distance.vertical.x! + 10\"\n [style.top.px]=\"(distance.vertical.y1! + distance.vertical.y2!) / 2\"\n >\n {{ formatValue(distance.vertical.value) }}\n </div>\n }\n }\n\n @if (enabled()) {\n @if (toolMode() === \"guides\") {\n @if (guidePreview(); as preview) {\n <div\n class=\"inspector-guide inspector-guide--preview\"\n [class.inspector-guide--vertical]=\"preview.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"\n preview.orientation === 'horizontal'\n \"\n [style.left.px]=\"\n preview.orientation === 'vertical' ? preview.position : null\n \"\n [style.top.px]=\"\n preview.orientation === 'horizontal' ? preview.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n >\n <span class=\"inspector-guide-line\"></span>\n </div>\n }\n }\n\n @for (guide of guides(); track guide.id) {\n <button\n type=\"button\"\n class=\"inspector-guide\"\n [class.inspector-guide--vertical]=\"guide.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"guide.orientation === 'horizontal'\"\n [class.inspector-guide--selected]=\"selectedGuideId() === guide.id\"\n [style.left.px]=\"\n guide.orientation === 'vertical' ? guide.position : null\n \"\n [style.top.px]=\"\n guide.orientation === 'horizontal' ? guide.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n (pointerdown)=\"startGuideDrag($event, guide)\"\n >\n <span class=\"inspector-guide-line\"></span>\n </button>\n }\n }\n</div>\n", styles: [":host{--inspector-panel: rgba(27, 29, 36, .94);--inspector-panel-edge: rgba(255, 255, 255, .08);--inspector-text: #f3f4f6;--inspector-muted: #9ca3af;--inspector-blue: #4f8cff;--inspector-orange: #f59e0b;--inspector-green: #22c55e;--inspector-violet: #a855f7;position:fixed;inset:0;z-index:2147;pointer-events:none;font-family:SF Pro Display,Segoe UI,sans-serif}.inspector-root{position:fixed;inset:0;pointer-events:none}.inspector-toolbar{position:fixed;top:12px;right:12px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail{display:flex;flex-direction:column;align-items:center;gap:2px;width:30px;padding:2px;border-radius:4px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.inspector-toolbar__divider{width:16px;height:1px;margin-block:2px;background:var(--inspector-panel-edge)}.inspector-toolbar__secondary{display:grid;width:24px}.inspector-toolbar__reveal{display:grid;grid-template-rows:0fr;opacity:0;transform:translateY(-4px);transition:grid-template-rows .18s cubic-bezier(.16,1,.3,1),opacity .12s cubic-bezier(.16,1,.3,1),transform .18s cubic-bezier(.16,1,.3,1)}.inspector-toolbar__reveal-inner{display:grid;gap:2px;min-height:0;overflow:hidden}.inspector-toolbar__rail:hover .inspector-toolbar__reveal,.inspector-toolbar__rail:focus-within .inspector-toolbar__reveal{grid-template-rows:1fr;opacity:1;transform:translateY(2px)}.inspector-checkpoint-group{position:relative;width:24px;height:24px}.inspector-checkpoint-panel{position:absolute;top:0;right:calc(100% + 8px);width:min(300px,calc(100vw - 24px));max-height:min(420px,calc(100vh - 64px));overflow:auto;border:1px solid var(--inspector-panel-edge);border-radius:8px;background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;color:var(--inspector-text)}.inspector-checkpoint-panel__header,.inspector-checkpoint-item,.inspector-checkpoint-actions{display:flex;align-items:center}.inspector-checkpoint-panel__header{justify-content:space-between;gap:8px;padding:6px 8px;border-bottom:1px solid var(--inspector-panel-edge)}.inspector-checkpoint-panel__header strong,.inspector-checkpoint-panel__header span{display:inline}.inspector-checkpoint-panel__header strong{font-size:12px}.inspector-checkpoint-panel__header span{margin-left:5px}.inspector-checkpoint-panel__header span,.inspector-checkpoint-empty,.inspector-checkpoint-message{color:var(--inspector-muted);font-size:10px;line-height:1.4}.inspector-checkpoint-save,.inspector-checkpoint-actions button,.inspector-checkpoint-name{border:0;background:transparent;color:inherit;cursor:pointer;font:inherit}.inspector-checkpoint-save{padding:5px 7px;border-radius:5px;background:#4f8cff2e;color:#dbeafe;font-size:10px}.inspector-checkpoint-list{display:grid}.inspector-checkpoint-item{justify-content:space-between;gap:8px;padding:5px 8px;border-bottom:1px solid rgba(255,255,255,.05)}.inspector-checkpoint-item__main{flex:1 1 auto;min-width:0}.inspector-checkpoint-name,.inspector-checkpoint-name-input{display:block;max-width:100%;overflow:hidden;padding:0;color:var(--inspector-text);font-size:11px;font-weight:600;text-align:left;text-overflow:ellipsis;white-space:nowrap}.inspector-checkpoint-name-input{width:100%;border:1px solid rgba(79,140,255,.6);border-radius:3px;background:#0003;outline:none}.inspector-checkpoint-actions{gap:2px;flex:0 0 auto}.inspector-checkpoint-actions button{padding:4px;border-radius:4px;color:var(--inspector-muted);font-size:10px}.inspector-checkpoint-actions button:hover,.inspector-checkpoint-name:hover,.inspector-checkpoint-save:hover{color:var(--inspector-text);background:#ffffff14}.inspector-checkpoint-empty,.inspector-checkpoint-message{margin:0;padding:12px 10px}.inspector-checkpoint-message--error{color:#fecaca}.inspector-icon-button{display:inline-flex;align-items:center;justify-content:center;height:24px;border:0;border-radius:3px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .1s cubic-bezier(.16,1,.3,1),color .1s cubic-bezier(.16,1,.3,1),transform .1s cubic-bezier(.16,1,.3,1);font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{position:relative;width:24px}.inspector-icon-button svg{width:13px;height:13px;fill:currentColor}.inspector-icon-button:active{transform:translateY(1px)}.inspector-icon-button:focus-visible{outline:2px solid var(--inspector-blue);outline-offset:2px}.inspector-icon-button:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--power:hover{background:#ef444429;color:#fecaca}.inspector-tooltip{position:absolute;top:50%;right:calc(100% + 7px);z-index:20;width:max-content;padding:4px 6px;border:1px solid var(--inspector-panel-edge);border-radius:3px;background:#111318fa;color:var(--inspector-text);font-size:10px;font-weight:600;line-height:1.2;opacity:0;pointer-events:none;transform:translate(3px,-50%);transition:opacity .12s cubic-bezier(.16,1,.3,1),transform .12s cubic-bezier(.16,1,.3,1);white-space:nowrap}.inspector-icon-button:hover .inspector-tooltip,.inspector-icon-button:focus-visible .inspector-tooltip{opacity:1;transform:translateY(-50%)}@media (prefers-reduced-motion: reduce){.inspector-toolbar__reveal,.inspector-icon-button,.inspector-tooltip{transition-duration:.01ms}}.inspector-box{position:fixed;box-sizing:border-box;pointer-events:none;border:1px solid var(--inspector-accent, var(--inspector-blue));background:color-mix(in srgb,var(--inspector-accent, var(--inspector-blue)) 8%,transparent)}.inspector-box--hover{opacity:.55}.inspector-box--selected{border-width:1.5px}.inspector-box--margin{border-color:#f59e0bd1;background:#f59e0b14}.inspector-box--padding{border-color:#22c55ed1;background:#22c55e14}.inspector-box--gap{border-color:#a855f7b8;background:#a855f70a}.inspector-box--distance{border-style:dashed;border-color:#4f8cffb3;background:transparent}.inspector-label,.inspector-box-tag,.inspector-text-chip,.inspector-distance-tag{font-variant-numeric:tabular-nums}.inspector-label{position:absolute;left:0;top:0;transform:translateY(calc(-100% - 8px));max-width:min(420px,calc(100vw - 24px));padding:6px 9px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);color:var(--inspector-text);box-shadow:0 18px 40px #00000042;font-size:11px;line-height:1;white-space:nowrap}.inspector-box-tag{position:fixed;transform:translateY(-100%);padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:var(--inspector-text);box-shadow:0 12px 24px #0003;font-size:10px;line-height:1;pointer-events:none}.inspector-box-tag--margin{color:#fbbf24}.inspector-box-tag--padding{color:#86efac;transform:translateY(0)}.inspector-box-tag--gap{color:#d8b4fe}.inspector-text-chip{position:fixed;transform:translateY(-100%);display:inline-flex;gap:6px;align-items:center;padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:#e5e7eb;box-shadow:0 12px 24px #00000038;font-size:10px;line-height:1;white-space:nowrap;pointer-events:none}.inspector-text-chip__swatch{width:8px;height:8px;border-radius:999px;border:1px solid rgba(255,255,255,.18);flex:0 0 auto}.inspector-connector{position:fixed;border-color:#4f8cffb8;border-style:dashed;pointer-events:none}.inspector-connector--vertical{border-left-width:1px;width:0}.inspector-connector--horizontal{border-top-width:1px;height:0}.inspector-distance-line{position:fixed;background:var(--inspector-blue);pointer-events:none}.inspector-distance-line--horizontal{height:1px}.inspector-distance-line--vertical{width:1px}.inspector-distance-tag{position:fixed;transform:translate(-50%);padding:5px 7px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f2;color:var(--inspector-text);font-size:10px;line-height:1;pointer-events:none}.inspector-distance-tag--vertical{transform:translateY(-50%)}.inspector-guide{position:fixed;margin:0;padding:0;border:0;background:transparent;pointer-events:auto;cursor:grab}.inspector-guide--vertical{top:0;width:14px;height:100vh;transform:translate(-50%)}.inspector-guide--horizontal{left:0;width:100vw;height:14px;transform:translateY(-50%)}.inspector-guide--preview{opacity:.55;pointer-events:none;cursor:default}.inspector-guide--preview .inspector-guide-line{background:color-mix(in srgb,var(--inspector-guide, #ff7a00) 72%,transparent)}.inspector-guide-line{position:absolute;inset:0;pointer-events:none}.inspector-guide--vertical .inspector-guide-line{left:calc(50% - .5px);width:1px;height:100%;background:var(--inspector-guide, #ff7a00)}.inspector-guide--horizontal .inspector-guide-line{top:calc(50% - .5px);width:100%;height:1px;background:var(--inspector-guide, #ff7a00)}.inspector-guide--selected .inspector-guide-line{box-shadow:0 0 0 1px #ffffff80}\n"] }]
|
|
1333
|
+
args: [{ selector: 'inspector-overlay', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"inspector-root\" #overlayRoot>\n @if (enabled()) {\n <div class=\"inspector-toolbar\">\n <div class=\"inspector-toolbar__rail\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'select'\"\n (click)=\"setToolMode('select')\"\n aria-label=\"Select mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 2l8 8-3 .7L6.7 13 6 10 3 2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Select</span>\n </button>\n\n @if (checkpointService) {\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n (click)=\"saveCheckpoint()\"\n [disabled]=\"checkpointService.busy()\"\n aria-label=\"Save checkpoint\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2.5 2h9.6l1.4 1.4V14h-11zm2 1.5v3h6v-3zm.5 6V12h6V9.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Save checkpoint</span>\n </button>\n }\n\n <div class=\"inspector-toolbar__secondary\">\n <div class=\"inspector-toolbar__reveal\">\n <div class=\"inspector-toolbar__reveal-inner\">\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n aria-label=\"Typography overlay\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Typography</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'vertical'\"\n (click)=\"activateGuideOrientation('vertical')\"\n aria-label=\"Vertical guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 2h1.5v12h-1.5z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Vertical guides</span>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"toolMode() === 'guides' && guideOrientation() === 'horizontal'\"\n (click)=\"activateGuideOrientation('horizontal')\"\n aria-label=\"Horizontal guides\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M2 7.25h12v1.5H2z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Horizontal guides</span>\n </button>\n </div>\n </div>\n </div>\n\n <span class=\"inspector-toolbar__divider\" aria-hidden=\"true\"></span>\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--power\"\n (click)=\"disableInspector()\"\n aria-label=\"Disable inspector; press M to enable\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M7.25 1.5h1.5v6h-1.5zM4.1 3.35l.96 1.15A4.25 4.25 0 1 0 10.94 4.5l.96-1.15A5.75 5.75 0 1 1 4.1 3.35z\" />\n </svg>\n <span class=\"inspector-tooltip\" role=\"tooltip\">Disable</span>\n </button>\n </div>\n </div>\n }\n\n @if (checkpointService) {\n <dialog\n #checkpointDialog\n class=\"inspector-command-dialog\"\n aria-labelledby=\"inspector-command-title\"\n (cancel)=\"$event.preventDefault(); closeCheckpointCommandBar()\"\n (click)=\"handleCheckpointDialogClick($event)\"\n >\n <section class=\"inspector-command\" (click)=\"$event.stopPropagation()\">\n <header class=\"inspector-command__search\">\n <label id=\"inspector-command-title\" class=\"inspector-visually-hidden\" for=\"inspector-checkpoint-search\">\n Search checkpoints\n </label>\n <input\n #checkpointSearch\n id=\"inspector-checkpoint-search\"\n type=\"search\"\n autocomplete=\"off\"\n spellcheck=\"false\"\n placeholder=\"Find a checkpoint\u2026\"\n [value]=\"checkpointQuery()\"\n [attr.aria-controls]=\"'inspector-checkpoint-results'\"\n [attr.aria-activedescendant]=\"filteredCheckpoints().length ? 'inspector-checkpoint-' + filteredCheckpoints()[activeCheckpointIndex()].id : null\"\n (input)=\"updateCheckpointQuery($event)\"\n />\n </header>\n\n @if (checkpointService.error(); as error) {\n <p class=\"inspector-command__message inspector-command__message--error\" role=\"alert\">{{ error }}</p>\n }\n\n <div id=\"inspector-checkpoint-results\" class=\"inspector-command__results\" role=\"listbox\" aria-label=\"Saved checkpoints\">\n @if (checkpointService.loading()) {\n <p class=\"inspector-command__message\" role=\"status\">Loading checkpoints\u2026</p>\n } @else if (!checkpointService.checkpoints().length) {\n <p class=\"inspector-command__message\" role=\"status\">No checkpoints yet. Save one from the Inspector toolbar.</p>\n } @else if (!filteredCheckpoints().length) {\n <p class=\"inspector-command__message\" role=\"status\">No checkpoint matches \u201C{{ checkpointQuery() }}\u201D.</p>\n } @else {\n @for (checkpoint of filteredCheckpoints(); track checkpoint.id; let index = $index) {\n <div\n class=\"inspector-command-row\"\n role=\"option\"\n [id]=\"'inspector-checkpoint-' + checkpoint.id\"\n [attr.data-checkpoint-index]=\"index\"\n [attr.aria-selected]=\"activeCheckpointIndex() === index\"\n [class.is-active]=\"activeCheckpointIndex() === index\"\n [class.is-busy]=\"checkpointService.busy() && activeCheckpointIndex() === index\"\n (mouseenter)=\"setActiveCheckpoint(index)\"\n (click)=\"deletingCheckpointId() === checkpoint.id || editingCheckpointId() === checkpoint.id ? null : restoreCheckpoint(checkpoint)\"\n >\n @if (deletingCheckpointId() === checkpoint.id) {\n <span class=\"inspector-command-row__confirm\">Delete \u201C{{ checkpoint.name }}\u201D?</span>\n <span class=\"inspector-command-row__confirm-actions\">\n <button type=\"button\" class=\"is-danger\" (click)=\"confirmDelete(checkpoint, $event)\">Delete checkpoint</button>\n <button type=\"button\" (click)=\"cancelDelete($event)\">Keep checkpoint</button>\n </span>\n } @else if (editingCheckpointId() === checkpoint.id) {\n <input\n class=\"inspector-command-row__rename\"\n aria-label=\"Checkpoint name\"\n [value]=\"renameDraft()\"\n (input)=\"updateRenameDraft($event)\"\n (click)=\"$event.stopPropagation()\"\n />\n } @else {\n <span class=\"inspector-command-row__name\">\n @if (checkpointService.busy() && activeCheckpointIndex() === index) {\n Restoring \u201C{{ checkpoint.name }}\u201D\u2026\n } @else {\n {{ checkpoint.name }}\n }\n </span>\n <span class=\"inspector-command-row__actions\">\n <button type=\"button\" (click)=\"beginRename(checkpoint, $event)\" [attr.aria-label]=\"'Rename ' + checkpoint.name\">\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"m11.8 1.8 2.4 2.4-8.3 8.3-3.1.7.7-3.1zm-8 10.4 1.5-.3-1.2-1.2z\" /></svg>\n </button>\n <button type=\"button\" (click)=\"beginDelete(checkpoint, $event)\" [attr.aria-label]=\"'Delete ' + checkpoint.name\">\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\"><path d=\"M5.5 2h5l.5 1H14v1.25H2V3h3zm-2 3.25h9L12 14H4z\" /></svg>\n </button>\n </span>\n }\n </div>\n }\n }\n </div>\n </section>\n </dialog>\n }\n\n @if (checkpointToast(); as toast) {\n <p\n class=\"inspector-checkpoint-toast\"\n [class.inspector-checkpoint-toast--error]=\"checkpointToastIsError()\"\n [attr.role]=\"checkpointToastIsError() ? 'alert' : 'status'\"\n >{{ toast }}</p>\n }\n\n @if (enabled() && toolMode() === \"select\") {\n @if (hoverRect() && hoverHighlightEnabled) {\n <div\n class=\"inspector-box inspector-box--hover\"\n [style.left.px]=\"hoverRect()!.left\"\n [style.top.px]=\"hoverRect()!.top\"\n [style.width.px]=\"hoverRect()!.width\"\n [style.height.px]=\"hoverRect()!.height\"\n [style.--inspector-accent]=\"highlightColor\"\n ></div>\n }\n\n @if (selectedMeasurement(); as selected) {\n @if (selected.gap.active && selected.parentRect) {\n <div\n class=\"inspector-box inspector-box--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top\"\n [style.width.px]=\"selected.parentRect.width\"\n [style.height.px]=\"selected.parentRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--gap\"\n [style.left.px]=\"selected.parentRect.left\"\n [style.top.px]=\"selected.parentRect.top - 8\"\n >\n G {{ formatGap(selected) }}\n </div>\n }\n\n <div\n class=\"inspector-box inspector-box--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top\"\n [style.width.px]=\"selected.marginRect.width\"\n [style.height.px]=\"selected.marginRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--margin\"\n [style.left.px]=\"selected.marginRect.left\"\n [style.top.px]=\"selected.marginRect.top - 8\"\n >\n M {{ formatEdges(selected.margin) }}\n </div>\n\n <div\n class=\"inspector-box inspector-box--selected\"\n [style.left.px]=\"selected.rect.left\"\n [style.top.px]=\"selected.rect.top\"\n [style.width.px]=\"selected.rect.width\"\n [style.height.px]=\"selected.rect.height\"\n [style.--inspector-accent]=\"highlightColor\"\n >\n <div class=\"inspector-label\">{{ selectedMetaLine() }}</div>\n </div>\n\n <div\n class=\"inspector-box inspector-box--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"selected.paddingRect.top\"\n [style.width.px]=\"selected.paddingRect.width\"\n [style.height.px]=\"selected.paddingRect.height\"\n ></div>\n <div\n class=\"inspector-box-tag inspector-box-tag--padding\"\n [style.left.px]=\"selected.paddingRect.left\"\n [style.top.px]=\"\n selected.paddingRect.top + selected.paddingRect.height + 8\n \"\n >\n P {{ formatEdges(selected.padding) }}\n </div>\n }\n }\n\n @if (enabled() && showTypography()) {\n @for (block of textBlocks(); track block.id) {\n <div\n class=\"inspector-text-chip\"\n [style.left.px]=\"block.rect.left\"\n [style.top.px]=\"block.rect.top - 8\"\n >\n <span\n class=\"inspector-text-chip__swatch\"\n [style.background]=\"block.styles.color\"\n ></span>\n <span>{{ block.styles.fontSize }}</span>\n <span>{{ block.styles.lineHeight }}</span>\n <span>{{ block.styles.color }}</span>\n </div>\n }\n }\n\n @if (enabled() && altPressed() && distanceOverlay(); as distance) {\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectA.left\"\n [style.top.px]=\"distance.rectA.top\"\n [style.width.px]=\"distance.rectA.width\"\n [style.height.px]=\"distance.rectA.height\"\n ></div>\n <div\n class=\"inspector-box inspector-box--distance\"\n [style.left.px]=\"distance.rectB.left\"\n [style.top.px]=\"distance.rectB.top\"\n [style.width.px]=\"distance.rectB.width\"\n [style.height.px]=\"distance.rectB.height\"\n ></div>\n\n @for (\n connector of distance.connectors;\n track trackConnector($index, connector)\n ) {\n <div\n class=\"inspector-connector\"\n [class.inspector-connector--vertical]=\"connector.x1 === connector.x2\"\n [class.inspector-connector--horizontal]=\"connector.y1 === connector.y2\"\n [style.left.px]=\"Math.min(connector.x1, connector.x2)\"\n [style.top.px]=\"Math.min(connector.y1, connector.y2)\"\n [style.width.px]=\"Math.max(1, Math.abs(connector.x2 - connector.x1))\"\n [style.height.px]=\"Math.max(1, Math.abs(connector.y2 - connector.y1))\"\n ></div>\n }\n\n @if (distance.horizontal && distance.horizontal.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--horizontal\"\n [style.left.px]=\"\n Math.min(distance.horizontal.x1!, distance.horizontal.x2!)\n \"\n [style.top.px]=\"distance.horizontal.y!\"\n [style.width.px]=\"\n Math.abs(distance.horizontal.x2! - distance.horizontal.x1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag\"\n [style.left.px]=\"\n (distance.horizontal.x1! + distance.horizontal.x2!) / 2\n \"\n [style.top.px]=\"distance.horizontal.y! + 10\"\n >\n {{ formatValue(distance.horizontal.value) }}\n </div>\n }\n\n @if (distance.vertical && distance.vertical.value > 0) {\n <div\n class=\"inspector-distance-line inspector-distance-line--vertical\"\n [style.left.px]=\"distance.vertical.x!\"\n [style.top.px]=\"Math.min(distance.vertical.y1!, distance.vertical.y2!)\"\n [style.height.px]=\"\n Math.abs(distance.vertical.y2! - distance.vertical.y1!)\n \"\n ></div>\n <div\n class=\"inspector-distance-tag inspector-distance-tag--vertical\"\n [style.left.px]=\"distance.vertical.x! + 10\"\n [style.top.px]=\"(distance.vertical.y1! + distance.vertical.y2!) / 2\"\n >\n {{ formatValue(distance.vertical.value) }}\n </div>\n }\n }\n\n @if (enabled()) {\n @if (toolMode() === \"guides\") {\n @if (guidePreview(); as preview) {\n <div\n class=\"inspector-guide inspector-guide--preview\"\n [class.inspector-guide--vertical]=\"preview.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"\n preview.orientation === 'horizontal'\n \"\n [style.left.px]=\"\n preview.orientation === 'vertical' ? preview.position : null\n \"\n [style.top.px]=\"\n preview.orientation === 'horizontal' ? preview.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n >\n <span class=\"inspector-guide-line\"></span>\n </div>\n }\n }\n\n @for (guide of guides(); track guide.id) {\n <button\n type=\"button\"\n class=\"inspector-guide\"\n [class.inspector-guide--vertical]=\"guide.orientation === 'vertical'\"\n [class.inspector-guide--horizontal]=\"guide.orientation === 'horizontal'\"\n [class.inspector-guide--selected]=\"selectedGuideId() === guide.id\"\n [style.left.px]=\"\n guide.orientation === 'vertical' ? guide.position : null\n \"\n [style.top.px]=\"\n guide.orientation === 'horizontal' ? guide.position : null\n \"\n [style.--inspector-guide]=\"guideColor\"\n (pointerdown)=\"startGuideDrag($event, guide)\"\n >\n <span class=\"inspector-guide-line\"></span>\n </button>\n }\n }\n</div>\n", styles: [":host{--inspector-panel: rgba(27, 29, 36, .94);--inspector-panel-edge: rgba(255, 255, 255, .08);--inspector-text: #f3f4f6;--inspector-muted: #9ca3af;--inspector-blue: #4f8cff;--inspector-orange: #f59e0b;--inspector-green: #22c55e;--inspector-violet: #a855f7;position:fixed;inset:0;z-index:2147;pointer-events:none;font-family:SF Pro Display,Segoe UI,sans-serif}.inspector-root{position:fixed;inset:0;pointer-events:none}.inspector-toolbar{position:fixed;top:12px;right:12px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail{display:flex;flex-direction:column;align-items:center;gap:2px;width:30px;padding:2px;border-radius:4px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);box-shadow:0 18px 40px #00000052;-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.inspector-toolbar__divider{width:16px;height:1px;margin-block:2px;background:var(--inspector-panel-edge)}.inspector-toolbar__secondary{display:grid;width:24px}.inspector-toolbar__reveal{display:grid;grid-template-rows:0fr;opacity:0;transform:translateY(-4px);transition:grid-template-rows .18s cubic-bezier(.16,1,.3,1),opacity .12s cubic-bezier(.16,1,.3,1),transform .18s cubic-bezier(.16,1,.3,1)}.inspector-toolbar__reveal-inner{display:grid;gap:2px;min-height:0;overflow:hidden}.inspector-toolbar__rail:hover .inspector-toolbar__reveal,.inspector-toolbar__rail:focus-within .inspector-toolbar__reveal{grid-template-rows:1fr;opacity:1;transform:translateY(2px)}.inspector-command-dialog{--command-surface: oklch(20% .012 270);--command-surface-raised: oklch(25% .014 270);--command-surface-active: oklch(27% .018 270);--command-edge: oklch(39% .014 270);--command-text: oklch(95% .008 270);--command-muted: oklch(73% .018 270);--command-accent: oklch(76% .08 260);--command-danger: oklch(71% .17 25);box-sizing:border-box;inset-block-start:max(40px,10vh);width:min(480px,calc(100vw - 24px));max-width:none;max-height:min(400px,calc(100vh - 64px));margin:0 auto;padding:0;overflow:hidden;border:1px solid var(--command-edge);border-radius:4px;background:var(--command-surface);color:var(--command-text);box-shadow:0 20px 56px #0102038f;opacity:0;pointer-events:auto;transform:translateY(-6px);transition:opacity .12s cubic-bezier(.7,0,.84,0),transform .12s cubic-bezier(.7,0,.84,0),overlay .12s allow-discrete,display .12s allow-discrete}.inspector-command-dialog[open]{opacity:1;transform:translateY(0);transition-duration:.16s;transition-timing-function:cubic-bezier(.16,1,.3,1)}@starting-style{.inspector-command-dialog[open]{opacity:0;transform:translateY(-6px)}}.inspector-command-dialog::backdrop{background:#0102037a}.inspector-command{display:grid;min-width:0}.inspector-command__search{min-height:44px;padding-inline:12px;border-bottom:1px solid var(--command-edge)}.inspector-command__search input,.inspector-command-row__rename{min-width:0;border:0;outline:0;background:transparent;color:var(--command-text);font:inherit}.inspector-command__search input{width:100%;height:43px;padding:0;font-size:14px;line-height:1.45}.inspector-command__search:has(input:focus-visible){box-shadow:inset 0 -2px var(--command-accent)}.inspector-command__search input::placeholder{color:var(--command-muted);opacity:1}.inspector-command__search input::-webkit-search-cancel-button{display:none}.inspector-command__results{max-height:304px;overflow:auto;padding-block:3px;scrollbar-color:var(--command-edge) transparent}.inspector-command-row{position:relative;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:6px;min-height:38px;padding-inline:12px 6px;cursor:pointer;-webkit-user-select:none;user-select:none}.inspector-command-row.is-active{background:var(--command-surface-active)}.inspector-command-row.is-busy{cursor:wait;pointer-events:none}.inspector-command-row__name{overflow:hidden;color:var(--command-text);font-size:12px;font-weight:540;line-height:1.45;text-overflow:ellipsis;white-space:nowrap}.inspector-command-row.is-active .inspector-command-row__name{font-weight:700}.inspector-command-row__actions,.inspector-command-row__confirm-actions{display:flex;align-items:center;gap:4px}.inspector-command-row__actions{opacity:0;pointer-events:none}.inspector-command-row:hover .inspector-command-row__actions,.inspector-command-row:focus-within .inspector-command-row__actions{opacity:1;pointer-events:auto}.inspector-command-row button{display:inline-grid;width:26px;min-height:26px;padding:5px;place-items:center;border:1px solid transparent;border-radius:3px;background:transparent;color:var(--command-muted);cursor:pointer;font-family:inherit;font-size:10px;font-weight:600;line-height:1.3}.inspector-command-row button svg{width:12px;height:12px;fill:currentColor}.inspector-command-row button:hover{border-color:var(--command-edge);background:var(--command-surface-raised);color:var(--command-text)}.inspector-command-row button.is-danger{color:var(--command-danger)}.inspector-command-row button:focus-visible,.inspector-command-row__rename:focus-visible{outline:2px solid var(--command-accent);outline-offset:2px}.inspector-command-row__confirm{grid-column:1;color:var(--command-text);font-size:12px;font-weight:620}.inspector-command-row__rename{grid-column:1 / -1;min-height:30px;padding-inline:8px;border:1px solid var(--command-accent);border-radius:3px;background:var(--command-surface-raised);font-size:12px}.inspector-command__message{margin:0;padding:20px 12px;color:var(--command-muted);font-size:12px;line-height:1.55;text-align:center}.inspector-command__message--error{padding-block:9px;border-bottom:1px solid oklch(45% .1 25);background:#3f1917;color:oklch(89% .06 25);text-align:left}.inspector-visually-hidden{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.inspector-checkpoint-toast{position:fixed;top:12px;left:50%;z-index:500;margin:0;padding:8px 12px;border:1px solid oklch(48% .04 150);border-radius:4px;background:#152619;color:#cfe9d4;box-shadow:0 16px 40px #01020375;font-size:11px;font-weight:620;line-height:1.35;pointer-events:none;transform:translate(-50%)}.inspector-checkpoint-toast--error{border-color:#9c433f;background:#431614;color:oklch(92% .05 25)}.inspector-icon-button{display:inline-flex;align-items:center;justify-content:center;height:24px;border:0;border-radius:3px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .1s cubic-bezier(.16,1,.3,1),color .1s cubic-bezier(.16,1,.3,1),transform .1s cubic-bezier(.16,1,.3,1);font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{position:relative;width:24px}.inspector-icon-button svg{width:13px;height:13px;fill:currentColor}.inspector-icon-button:active{transform:translateY(1px)}.inspector-icon-button:focus-visible{outline:2px solid var(--inspector-blue);outline-offset:2px}.inspector-icon-button:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--power:hover{background:#ef444429;color:#fecaca}.inspector-tooltip{position:absolute;top:50%;right:calc(100% + 7px);z-index:20;width:max-content;padding:4px 6px;border:1px solid var(--inspector-panel-edge);border-radius:3px;background:#111318fa;color:var(--inspector-text);font-size:10px;font-weight:600;line-height:1.2;opacity:0;pointer-events:none;transform:translate(3px,-50%);transition:opacity .12s cubic-bezier(.16,1,.3,1),transform .12s cubic-bezier(.16,1,.3,1);white-space:nowrap}.inspector-icon-button:hover .inspector-tooltip,.inspector-icon-button:focus-visible .inspector-tooltip{opacity:1;transform:translateY(-50%)}@media (prefers-reduced-motion: reduce){.inspector-toolbar__reveal,.inspector-icon-button,.inspector-tooltip,.inspector-command-dialog,.inspector-command-row__marker{transition-duration:.01ms}.inspector-command-dialog,.inspector-command-dialog[open]{transform:none}}.inspector-box{position:fixed;box-sizing:border-box;pointer-events:none;border:1px solid var(--inspector-accent, var(--inspector-blue));background:color-mix(in srgb,var(--inspector-accent, var(--inspector-blue)) 8%,transparent)}.inspector-box--hover{opacity:.55}.inspector-box--selected{border-width:1.5px}.inspector-box--margin{border-color:#f59e0bd1;background:#f59e0b14}.inspector-box--padding{border-color:#22c55ed1;background:#22c55e14}.inspector-box--gap{border-color:#a855f7b8;background:#a855f70a}.inspector-box--distance{border-style:dashed;border-color:#4f8cffb3;background:transparent}.inspector-label,.inspector-box-tag,.inspector-text-chip,.inspector-distance-tag{font-variant-numeric:tabular-nums}.inspector-label{position:absolute;left:0;top:0;transform:translateY(calc(-100% - 8px));max-width:min(420px,calc(100vw - 24px));padding:6px 9px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:var(--inspector-panel);color:var(--inspector-text);box-shadow:0 18px 40px #00000042;font-size:11px;line-height:1;white-space:nowrap}.inspector-box-tag{position:fixed;transform:translateY(-100%);padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:var(--inspector-text);box-shadow:0 12px 24px #0003;font-size:10px;line-height:1;pointer-events:none}.inspector-box-tag--margin{color:#fbbf24}.inspector-box-tag--padding{color:#86efac;transform:translateY(0)}.inspector-box-tag--gap{color:#d8b4fe}.inspector-text-chip{position:fixed;transform:translateY(-100%);display:inline-flex;gap:6px;align-items:center;padding:4px 6px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f0;color:#e5e7eb;box-shadow:0 12px 24px #00000038;font-size:10px;line-height:1;white-space:nowrap;pointer-events:none}.inspector-text-chip__swatch{width:8px;height:8px;border-radius:999px;border:1px solid rgba(255,255,255,.18);flex:0 0 auto}.inspector-connector{position:fixed;border-color:#4f8cffb8;border-style:dashed;pointer-events:none}.inspector-connector--vertical{border-left-width:1px;width:0}.inspector-connector--horizontal{border-top-width:1px;height:0}.inspector-distance-line{position:fixed;background:var(--inspector-blue);pointer-events:none}.inspector-distance-line--horizontal{height:1px}.inspector-distance-line--vertical{width:1px}.inspector-distance-tag{position:fixed;transform:translate(-50%);padding:5px 7px;border-radius:999px;border:1px solid var(--inspector-panel-edge);background:#111827f2;color:var(--inspector-text);font-size:10px;line-height:1;pointer-events:none}.inspector-distance-tag--vertical{transform:translateY(-50%)}.inspector-guide{position:fixed;margin:0;padding:0;border:0;background:transparent;pointer-events:auto;cursor:grab}.inspector-guide--vertical{top:0;width:14px;height:100vh;transform:translate(-50%)}.inspector-guide--horizontal{left:0;width:100vw;height:14px;transform:translateY(-50%)}.inspector-guide--preview{opacity:.55;pointer-events:none;cursor:default}.inspector-guide--preview .inspector-guide-line{background:color-mix(in srgb,var(--inspector-guide, #ff7a00) 72%,transparent)}.inspector-guide-line{position:absolute;inset:0;pointer-events:none}.inspector-guide--vertical .inspector-guide-line{left:calc(50% - .5px);width:1px;height:100%;background:var(--inspector-guide, #ff7a00)}.inspector-guide--horizontal .inspector-guide-line{top:calc(50% - .5px);width:100%;height:1px;background:var(--inspector-guide, #ff7a00)}.inspector-guide--selected .inspector-guide-line{box-shadow:0 0 0 1px #ffffff80}\n"] }]
|
|
1050
1334
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
1051
1335
|
type: Inject,
|
|
1052
1336
|
args: [PLATFORM_ID]
|
|
@@ -1085,5 +1369,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1085
1369
|
* Generated bundle index. Do not edit.
|
|
1086
1370
|
*/
|
|
1087
1371
|
|
|
1088
|
-
export { GUIDE_HITBOX_SIZE, GUIDE_SNAP_DISTANCE,
|
|
1372
|
+
export { GUIDE_HITBOX_SIZE, GUIDE_SNAP_DISTANCE, clamp, createId, formatValue, getDistanceOverlay, getElementLabel, getInspectMeasurement, getRectFromDom, getTargetElement, getTextInspection, getViewportSize, getVisibleTextBlocks, inspectorComponent, inspector_STATE_VERSION, inspector_STORAGE_KEY, provideInspectorCheckpoints, rectContainsPoint, rectsEqual };
|
|
1089
1373
|
//# sourceMappingURL=inspector-ng.mjs.map
|