inspector-ng 0.0.6 → 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 +28 -1
- package/esm2022/lib/checkpoint-search.mjs +50 -0
- package/esm2022/lib/checkpoints.mjs +323 -0
- package/esm2022/lib/constants.mjs +2 -2
- package/esm2022/lib/inspector.component.mjs +248 -22
- package/esm2022/lib/types.mjs +1 -1
- package/esm2022/public-api.mjs +2 -1
- package/fesm2022/inspector-ng.mjs +617 -23
- package/fesm2022/inspector-ng.mjs.map +1 -1
- package/lib/checkpoint-search.d.ts +3 -0
- package/lib/checkpoints.d.ts +58 -0
- package/lib/constants.d.ts +1 -1
- package/lib/inspector.component.d.ts +35 -2
- package/lib/types.d.ts +4 -1
- package/package.json +5 -2
- package/public-api.d.ts +1 -0
|
@@ -1,12 +1,332 @@
|
|
|
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 i2 from '@angular/router';
|
|
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';
|
|
4
9
|
|
|
5
10
|
const inspector_STORAGE_KEY = "inspector-state";
|
|
6
|
-
const inspector_STATE_VERSION =
|
|
11
|
+
const inspector_STATE_VERSION = 2;
|
|
7
12
|
const GUIDE_SNAP_DISTANCE = 10;
|
|
8
13
|
const GUIDE_HITBOX_SIZE = 14;
|
|
9
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;
|
|
100
|
+
this.router = router;
|
|
101
|
+
this.platformId = platformId;
|
|
102
|
+
this.checkpoints = signal([]);
|
|
103
|
+
this.loading = signal(false);
|
|
104
|
+
this.busy = signal(false);
|
|
105
|
+
this.error = signal(null);
|
|
106
|
+
}
|
|
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));
|
|
112
|
+
}
|
|
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);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
async save() {
|
|
121
|
+
this.error.set(null);
|
|
122
|
+
if (!this.store) {
|
|
123
|
+
this.error.set(this.missingStoreMessage());
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
this.busy.set(true);
|
|
127
|
+
try {
|
|
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();
|
|
133
|
+
const checkpoint = {
|
|
134
|
+
version: 1,
|
|
135
|
+
id: createCheckpointId(),
|
|
136
|
+
name: nextAutomaticName(pathname, persisted.map(({ name }) => name)),
|
|
137
|
+
route,
|
|
138
|
+
createdAt: new Date().toISOString(),
|
|
139
|
+
state,
|
|
140
|
+
};
|
|
141
|
+
await this.repository.put(checkpoint);
|
|
142
|
+
this.checkpoints.set([checkpoint, ...persisted].sort(newestFirst$1));
|
|
143
|
+
return checkpoint;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
this.error.set(this.storageMessage(error, 'The checkpoint could not be saved.'));
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
finally {
|
|
150
|
+
this.busy.set(false);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
async restore(id) {
|
|
154
|
+
this.error.set(null);
|
|
155
|
+
if (!this.store) {
|
|
156
|
+
this.error.set(this.missingStoreMessage());
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
this.busy.set(true);
|
|
160
|
+
try {
|
|
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.`);
|
|
168
|
+
}
|
|
169
|
+
return true;
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
this.error.set(this.messageFor(error, 'The checkpoint could not be restored. Delete it and save a new one.'));
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
finally {
|
|
176
|
+
this.busy.set(false);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
async rename(id, name) {
|
|
180
|
+
this.error.set(null);
|
|
181
|
+
const nextName = name.trim();
|
|
182
|
+
if (!nextName) {
|
|
183
|
+
this.error.set('Please enter a checkpoint name.');
|
|
184
|
+
return false;
|
|
185
|
+
}
|
|
186
|
+
this.busy.set(true);
|
|
187
|
+
try {
|
|
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.`);
|
|
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;
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
this.error.set(this.storageMessage(error, 'The checkpoint could not be renamed.'));
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
this.busy.set(false);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
async delete(id) {
|
|
208
|
+
this.error.set(null);
|
|
209
|
+
this.busy.set(true);
|
|
210
|
+
try {
|
|
211
|
+
await this.repository.delete(id);
|
|
212
|
+
this.checkpoints.set(this.checkpoints().filter((item) => item.id !== id));
|
|
213
|
+
return true;
|
|
214
|
+
}
|
|
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);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
clearError() {
|
|
224
|
+
this.error.set(null);
|
|
225
|
+
}
|
|
226
|
+
currentRoute() {
|
|
227
|
+
if (!isPlatformBrowser(this.platformId))
|
|
228
|
+
return '/';
|
|
229
|
+
return `${this.currentPathname()}${window.location.search}${window.location.hash}`;
|
|
230
|
+
}
|
|
231
|
+
currentPathname() {
|
|
232
|
+
if (!isPlatformBrowser(this.platformId))
|
|
233
|
+
return '/';
|
|
234
|
+
return window.location.pathname || '/';
|
|
235
|
+
}
|
|
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);
|
|
244
|
+
}
|
|
245
|
+
messageFor(error, fallback) {
|
|
246
|
+
return error instanceof Error && error.message ? error.message : fallback;
|
|
247
|
+
}
|
|
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 }); }
|
|
250
|
+
}
|
|
251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: InspectorCheckpointService, decorators: [{
|
|
252
|
+
type: Injectable
|
|
253
|
+
}], ctorParameters: () => [{ type: InspectorCheckpointRepository, decorators: [{
|
|
254
|
+
type: Inject,
|
|
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
|
+
}, {
|
|
264
|
+
type: Inject,
|
|
265
|
+
args: [Router]
|
|
266
|
+
}] }, { type: undefined, decorators: [{
|
|
267
|
+
type: Inject,
|
|
268
|
+
args: [PLATFORM_ID]
|
|
269
|
+
}] }] });
|
|
270
|
+
function provideInspectorCheckpoints() {
|
|
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
|
+
]);
|
|
284
|
+
}
|
|
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;
|
|
328
|
+
}
|
|
329
|
+
|
|
10
330
|
const clamp = (value, min, max) => Math.max(min, Math.min(max, value));
|
|
11
331
|
const getViewportSize = () => ({
|
|
12
332
|
width: typeof window === 'undefined' ? 1 : window.innerWidth || 1,
|
|
@@ -267,6 +587,56 @@ const getTargetElement = (point, overlayElement) => {
|
|
|
267
587
|
return document.elementFromPoint(point.x, point.y);
|
|
268
588
|
};
|
|
269
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
|
+
|
|
270
640
|
const createId = () => typeof crypto !== 'undefined' && 'randomUUID' in crypto
|
|
271
641
|
? crypto.randomUUID()
|
|
272
642
|
: `${Date.now()}-${Math.random()}`;
|
|
@@ -278,22 +648,33 @@ class inspectorComponent {
|
|
|
278
648
|
this.formatValue = formatValue;
|
|
279
649
|
this.guideHitboxSize = GUIDE_HITBOX_SIZE;
|
|
280
650
|
this.overlayRoot = viewChild('overlayRoot');
|
|
651
|
+
this.checkpointDialog = viewChild('checkpointDialog');
|
|
652
|
+
this.checkpointSearch = viewChild('checkpointSearch');
|
|
281
653
|
this.highlightColor = '#4f8cff';
|
|
282
654
|
this.guideColor = '#ff7a00';
|
|
283
655
|
this.hoverHighlightEnabled = true;
|
|
284
656
|
this.persistOnReload = false;
|
|
285
|
-
this.
|
|
657
|
+
this.checkpointService = inject(InspectorCheckpointService, { optional: true });
|
|
658
|
+
this.commandBarOpen = signal(false);
|
|
659
|
+
this.checkpointQuery = signal('');
|
|
660
|
+
this.activeCheckpointIndex = signal(0);
|
|
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()));
|
|
667
|
+
this.enabled = signal(true);
|
|
286
668
|
this.toolMode = signal('none');
|
|
287
669
|
this.guideOrientation = signal('vertical');
|
|
288
670
|
this.guides = signal([]);
|
|
289
671
|
this.hoverRect = signal(null);
|
|
290
672
|
this.selectedGuideId = signal(null);
|
|
291
673
|
this.selectedMeasurement = signal(null);
|
|
292
|
-
this.showTypography = signal(
|
|
674
|
+
this.showTypography = signal(false);
|
|
293
675
|
this.altPressed = signal(false);
|
|
294
676
|
this.distanceOverlay = signal(null);
|
|
295
677
|
this.textBlocks = signal([]);
|
|
296
|
-
this.guideMenuOpen = signal(false);
|
|
297
678
|
this.guidePreview = signal(null);
|
|
298
679
|
this.selectedMetaLine = computed(() => {
|
|
299
680
|
const selected = this.selectedMeasurement();
|
|
@@ -318,6 +699,7 @@ class inspectorComponent {
|
|
|
318
699
|
this.draggingGuideId = null;
|
|
319
700
|
this.selectedElement = null;
|
|
320
701
|
this.hoverElement = null;
|
|
702
|
+
this.toastTimer = null;
|
|
321
703
|
this.canUndo = computed(() => this.historyIndex() > 0);
|
|
322
704
|
this.canRedo = computed(() => this.historyIndex() >= 0 &&
|
|
323
705
|
this.historyIndex() < this.history().length - 1);
|
|
@@ -359,12 +741,12 @@ class inspectorComponent {
|
|
|
359
741
|
}
|
|
360
742
|
try {
|
|
361
743
|
const parsed = JSON.parse(stored);
|
|
362
|
-
if (parsed.version === inspector_STATE_VERSION) {
|
|
363
|
-
this.enabled.set(
|
|
744
|
+
if (parsed.version === inspector_STATE_VERSION || parsed.version === 1) {
|
|
745
|
+
this.enabled.set(true);
|
|
364
746
|
this.toolMode.set(parsed.toolMode);
|
|
365
747
|
this.guideOrientation.set(parsed.guideOrientation);
|
|
366
748
|
this.guides.set(parsed.guides ?? []);
|
|
367
|
-
this.showTypography.set(parsed.showTypography ??
|
|
749
|
+
this.showTypography.set(parsed.version === 1 ? false : (parsed.showTypography ?? false));
|
|
368
750
|
this.recordHistory(parsed.guides ?? []);
|
|
369
751
|
}
|
|
370
752
|
}
|
|
@@ -381,6 +763,8 @@ class inspectorComponent {
|
|
|
381
763
|
window.removeEventListener('click', this.handleCapturedClick, {
|
|
382
764
|
capture: true,
|
|
383
765
|
});
|
|
766
|
+
if (this.toastTimer)
|
|
767
|
+
clearTimeout(this.toastTimer);
|
|
384
768
|
}
|
|
385
769
|
setToolMode(mode) {
|
|
386
770
|
if (!this.enabled()) {
|
|
@@ -396,12 +780,125 @@ class inspectorComponent {
|
|
|
396
780
|
this.guideOrientation.set(orientation);
|
|
397
781
|
this.guidePreview.set(null);
|
|
398
782
|
}
|
|
783
|
+
activateGuideOrientation(orientation) {
|
|
784
|
+
const isActive = this.toolMode() === 'guides' && this.guideOrientation() === orientation;
|
|
785
|
+
this.setGuideOrientation(orientation);
|
|
786
|
+
if (isActive || this.toolMode() !== 'guides') {
|
|
787
|
+
this.setToolMode('guides');
|
|
788
|
+
}
|
|
789
|
+
}
|
|
399
790
|
toggleTypography() {
|
|
400
791
|
this.showTypography.update((value) => !value);
|
|
401
792
|
this.refreshTypographyBlocks();
|
|
402
793
|
}
|
|
403
|
-
|
|
404
|
-
this.
|
|
794
|
+
disableInspector() {
|
|
795
|
+
this.setInspectorEnabled(false);
|
|
796
|
+
}
|
|
797
|
+
async saveCheckpoint() {
|
|
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
|
+
}
|
|
805
|
+
}
|
|
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);
|
|
827
|
+
}
|
|
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);
|
|
837
|
+
}
|
|
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;
|
|
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
|
+
});
|
|
860
|
+
}
|
|
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
|
+
}
|
|
870
|
+
}
|
|
871
|
+
cancelRename() {
|
|
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
|
+
}
|
|
405
902
|
}
|
|
406
903
|
clearGuides() {
|
|
407
904
|
this.guides.set([]);
|
|
@@ -422,18 +919,27 @@ class inspectorComponent {
|
|
|
422
919
|
}
|
|
423
920
|
handleKeydown(event) {
|
|
424
921
|
const key = event.key.toLowerCase();
|
|
425
|
-
if (
|
|
922
|
+
if (this.checkpointService &&
|
|
923
|
+
!event.repeat &&
|
|
924
|
+
(event.metaKey || event.ctrlKey) &&
|
|
925
|
+
event.shiftKey &&
|
|
926
|
+
key === 'p') {
|
|
426
927
|
event.preventDefault();
|
|
427
|
-
this.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
928
|
+
this.commandBarOpen() ? this.closeCheckpointCommandBar() : void this.openCheckpointCommandBar();
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
if (this.commandBarOpen()) {
|
|
932
|
+
this.handleCommandBarKeydown(event, key);
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
if (key === 'm') {
|
|
936
|
+
const target = event.target;
|
|
937
|
+
if (target?.matches('input, textarea, select') ||
|
|
938
|
+
target?.isContentEditable) {
|
|
939
|
+
return;
|
|
435
940
|
}
|
|
436
|
-
|
|
941
|
+
event.preventDefault();
|
|
942
|
+
this.setInspectorEnabled(!this.enabled());
|
|
437
943
|
return;
|
|
438
944
|
}
|
|
439
945
|
if (!this.enabled()) {
|
|
@@ -588,6 +1094,94 @@ class inspectorComponent {
|
|
|
588
1094
|
const overlay = this.overlayRoot()?.nativeElement ?? null;
|
|
589
1095
|
return !(overlay && overlay.contains(event.target));
|
|
590
1096
|
}
|
|
1097
|
+
setInspectorEnabled(enabled) {
|
|
1098
|
+
this.enabled.set(enabled);
|
|
1099
|
+
if (!enabled) {
|
|
1100
|
+
this.toolMode.set('none');
|
|
1101
|
+
this.hoverRect.set(null);
|
|
1102
|
+
this.selectedGuideId.set(null);
|
|
1103
|
+
this.selectedMeasurement.set(null);
|
|
1104
|
+
this.distanceOverlay.set(null);
|
|
1105
|
+
this.textBlocks.set([]);
|
|
1106
|
+
this.guidePreview.set(null);
|
|
1107
|
+
this.altPressed.set(false);
|
|
1108
|
+
this.selectedElement = null;
|
|
1109
|
+
this.hoverElement = null;
|
|
1110
|
+
this.draggingGuideId = null;
|
|
1111
|
+
}
|
|
1112
|
+
this.refreshTypographyBlocks();
|
|
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
|
+
}
|
|
591
1185
|
selectElementAtPoint(event) {
|
|
592
1186
|
const overlay = this.overlayRoot()?.nativeElement ?? null;
|
|
593
1187
|
const target = getTargetElement({ x: event.clientX, y: event.clientY }, overlay);
|
|
@@ -732,11 +1326,11 @@ class inspectorComponent {
|
|
|
732
1326
|
return null;
|
|
733
1327
|
}
|
|
734
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 }); }
|
|
735
|
-
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 <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 title=\"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 </button>\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n title=\"Typography mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n </button>\n <div class=\"inspector-guide-group\">\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--guide-main\"\n [class.is-active]=\"toolMode() === 'guides'\"\n (click)=\"setToolMode('guides')\"\n title=\"Guides mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 2h1v12H4zM11 2h1v12h-1zM2 4h12v1H2zM2 11h12v1H2z\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--ghost inspector-icon-button--guide-menu\"\n [class.is-active]=\"guideMenuOpen()\"\n (click)=\"toggleGuideMenu()\"\n title=\"Guide options\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.47 6.97L8 10.5l3.53-3.53-.94-.94L8 8.62 5.41 6.03z\" />\n </svg>\n </button>\n\n @if (guideMenuOpen()) {\n <div class=\"inspector-guide-menu\">\n <button\n type=\"button\"\n class=\"inspector-segment\"\n [class.is-active]=\"guideOrientation() === 'vertical'\"\n (click)=\"setGuideOrientation('vertical')\"\n >\n Vertical\n </button>\n <button\n type=\"button\"\n class=\"inspector-segment\"\n [class.is-active]=\"guideOrientation() === 'horizontal'\"\n (click)=\"setGuideOrientation('horizontal')\"\n >\n Horizontal\n </button>\n <button\n type=\"button\"\n class=\"inspector-segment inspector-segment--danger\"\n (click)=\"clearGuides()\"\n >\n Clear\n </button>\n </div>\n }\n </div>\n </div>\n </div>\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:18px;right:18px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail,.inspector-guide-menu{display:flex;gap:4px;padding:4px;border-radius:8px;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__rail{align-items:center}.inspector-guide-group{position:relative;display:flex;gap:0;align-items:center;border-radius:6px}.inspector-guide-menu{position:absolute;top:calc(100% + 8px);right:0;min-width:max-content;flex-direction:column;align-items:stretch}.inspector-guide-menu .inspector-segment{justify-content:flex-start;width:100%}.inspector-icon-button,.inspector-segment{display:inline-flex;align-items:center;justify-content:center;height:26px;border:0;border-radius:6px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .12s ease,color .12s ease;font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{width:26px}.inspector-icon-button svg{width:14px;height:14px;fill:currentColor}.inspector-icon-button:hover,.inspector-segment:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active,.inspector-segment.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--ghost.is-active{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button--guide-main{border-radius:6px 0 0 6px}.inspector-icon-button--guide-menu{width:20px;border-radius:0 6px 6px 0}.inspector-icon-button--guide-menu svg{width:12px;height:12px}.inspector-segment{padding:0 8px;white-space:nowrap}.inspector-segment--danger:hover{background:#ef444429;color:#fecaca}.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 }); }
|
|
736
1330
|
}
|
|
737
1331
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: inspectorComponent, decorators: [{
|
|
738
1332
|
type: Component,
|
|
739
|
-
args: [{ selector: 'inspector-overlay', standalone: true, imports: [], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"inspector-root\" #overlayRoot>\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 title=\"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 </button>\n <button\n type=\"button\"\n class=\"inspector-icon-button\"\n [class.is-active]=\"showTypography()\"\n (click)=\"toggleTypography()\"\n title=\"Typography mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M3 4V2h10v2h-4v10H7V4z\" />\n </svg>\n </button>\n <div class=\"inspector-guide-group\">\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--guide-main\"\n [class.is-active]=\"toolMode() === 'guides'\"\n (click)=\"setToolMode('guides')\"\n title=\"Guides mode\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4 2h1v12H4zM11 2h1v12h-1zM2 4h12v1H2zM2 11h12v1H2z\" />\n </svg>\n </button>\n\n <button\n type=\"button\"\n class=\"inspector-icon-button inspector-icon-button--ghost inspector-icon-button--guide-menu\"\n [class.is-active]=\"guideMenuOpen()\"\n (click)=\"toggleGuideMenu()\"\n title=\"Guide options\"\n >\n <svg viewBox=\"0 0 16 16\" aria-hidden=\"true\">\n <path d=\"M4.47 6.97L8 10.5l3.53-3.53-.94-.94L8 8.62 5.41 6.03z\" />\n </svg>\n </button>\n\n @if (guideMenuOpen()) {\n <div class=\"inspector-guide-menu\">\n <button\n type=\"button\"\n class=\"inspector-segment\"\n [class.is-active]=\"guideOrientation() === 'vertical'\"\n (click)=\"setGuideOrientation('vertical')\"\n >\n Vertical\n </button>\n <button\n type=\"button\"\n class=\"inspector-segment\"\n [class.is-active]=\"guideOrientation() === 'horizontal'\"\n (click)=\"setGuideOrientation('horizontal')\"\n >\n Horizontal\n </button>\n <button\n type=\"button\"\n class=\"inspector-segment inspector-segment--danger\"\n (click)=\"clearGuides()\"\n >\n Clear\n </button>\n </div>\n }\n </div>\n </div>\n </div>\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:18px;right:18px;display:block;pointer-events:auto;z-index:10000}.inspector-toolbar__rail,.inspector-guide-menu{display:flex;gap:4px;padding:4px;border-radius:8px;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__rail{align-items:center}.inspector-guide-group{position:relative;display:flex;gap:0;align-items:center;border-radius:6px}.inspector-guide-menu{position:absolute;top:calc(100% + 8px);right:0;min-width:max-content;flex-direction:column;align-items:stretch}.inspector-guide-menu .inspector-segment{justify-content:flex-start;width:100%}.inspector-icon-button,.inspector-segment{display:inline-flex;align-items:center;justify-content:center;height:26px;border:0;border-radius:6px;background:transparent;color:var(--inspector-muted);cursor:pointer;transition:background .12s ease,color .12s ease;font:inherit;font-size:11px;letter-spacing:.02em}.inspector-icon-button{width:26px}.inspector-icon-button svg{width:14px;height:14px;fill:currentColor}.inspector-icon-button:hover,.inspector-segment:hover{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button.is-active,.inspector-segment.is-active{background:#4f8cff2e;color:#dbeafe}.inspector-icon-button--ghost.is-active{background:#ffffff14;color:var(--inspector-text)}.inspector-icon-button--guide-main{border-radius:6px 0 0 6px}.inspector-icon-button--guide-menu{width:20px;border-radius:0 6px 6px 0}.inspector-icon-button--guide-menu svg{width:12px;height:12px}.inspector-segment{padding:0 8px;white-space:nowrap}.inspector-segment--danger:hover{background:#ef444429;color:#fecaca}.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"] }]
|
|
740
1334
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
741
1335
|
type: Inject,
|
|
742
1336
|
args: [PLATFORM_ID]
|
|
@@ -775,5 +1369,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
775
1369
|
* Generated bundle index. Do not edit.
|
|
776
1370
|
*/
|
|
777
1371
|
|
|
778
|
-
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, rectContainsPoint, rectsEqual };
|
|
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 };
|
|
779
1373
|
//# sourceMappingURL=inspector-ng.mjs.map
|