@selimssevgi/cube-design-angular 0.5.1 → 0.6.0
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/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@
|
|
|
4
4
|
|
|
5
5
|
nothing yet.
|
|
6
6
|
|
|
7
|
+
## 0.6.0 (proposed, 4 november 2026: the report kit, so mosaic, cubes and anlat let anyone tell us something)
|
|
8
|
+
|
|
9
|
+
- **telling us**, the report kit (`ReportKind`, `REPORT_KINDS`, `ReportBuild`, `ReportApp`, `REPORT_APP`, `ReportDiagnostics`, `ReportErrorHandler`, `reportInterceptor`, `provideReportDiagnostics`, `REPORT_INCIDENTS_KEPT`, `REPORT_INCIDENTS_MS`, `scrubbedPath`, `errorLine`, `ReportContext`, `reportContext`, `contextOf`, `routePattern`, `environmentOf`, `technicalLines`, `ReportDraft`, `findJustSaved`, `JUST_SAVED_MINUTES`, `SavedReportLike`, `ReportClient`, `ReportInput`, `ReportRequest`, `SavedReport`, `reportRequest`, `reportRefusal`): mosaic's feedback capture made headless (lang-app-fe `core/feedback`), so every cube app gathers the same and sends it to the backend's `POST /feedback`, the one door every signed-in account may use, into the owner's todarch (the study *feedback in every cube app*, step B). it gathers the app, the app's own words for the screen, the route pattern, the page's address without its query or fragment, the environment, the held piece's key, the build and device and, for a bug, the last 5 errors and failed requests of 10 minutes; never a heading or a name a person wrote. the draft is kept on the phone; sending again is never twice (the backend answers the same words from the same reporter within 15 minutes with the same report; `justSaved` looks first where the account may read the reports); a refusal says why in the backend's words (`reportRefusal`); and `undo`. a report may be not known yet (`kind: null`, no `type`, no kind placed). no words, no sheet.
|
|
10
|
+
- **tell**, the last part of every context cube (selim's decision of 4 november 2026): `ContextPartKey` has `tell`, `CONTEXT_PART_ORDER` ends with it and `orderParts` keeps it last, after the parts an app adds (`CONTEXT_LAST_PARTS`). `tellLevel(words)` (`TellData`) gives its four pieces, something broke, make it better, an idea and not known yet, in `ContextWords.tell` (`TellWords`); a tap ends the journey and the app opens its own sheet. `<cube-icon>` draws `tell`, `bug`, `improvement`, `idea` and `not-known`. an app that does not add tell sees no change.
|
|
11
|
+
- `@angular/router` is a peer dependency now (`reportContext` reads the route pattern); every app already has it.
|
|
12
|
+
|
|
7
13
|
## 0.5.1 (4 november 2026: an empty piece has room)
|
|
8
14
|
|
|
9
15
|
- **`least` on `<cube-square>`** and the pure `squareLeast(side, { cell, min, max })` (`SQUARE_LEAST_CELL`, 200): a dashed square (something can be added) stands at least as many across as keep a place near 200 px, 3 × 3 on a phone, 4 × 4 in a desktop column, 5 × 5 at the widest; past that the tiles grow it as before. an empty piece is its own box first, then dashed places, never one place as wide as the screen (selim's pick, way c of `studies/empty-piece-places.md`, 4 november 2026: "this single add looks ridiculous"). `squareLayout` takes `least` too. on by default (`'room'`) for every dashed square; `[least]="0"` keeps the smallest. a square that is not dashed never changes.
|
package/README.md
CHANGED
|
@@ -241,7 +241,8 @@ what a tap asks (`tap`: `stand` or `take-out`) and why it would be refused (`ref
|
|
|
241
241
|
`inside-itself`, `given-place`, `last-place`, or the app's own key). `whereLevel(places, words.where)`
|
|
242
242
|
makes context tiles of them (`zoom` on the deeper ones, which `<cube-context>` draws as a › that
|
|
243
243
|
enters that path without asking `pick`). `insideOf`, `wouldLoop` and `refusalOf` are exported for an
|
|
244
|
-
app's own drop sheet. the part is `where`, after move, before whose and hide.
|
|
244
|
+
app's own drop sheet. the part is `where`, after move, before whose and hide; tell (0.6.0) is last of
|
|
245
|
+
all, after the parts an app adds.
|
|
245
246
|
|
|
246
247
|
## the context cube
|
|
247
248
|
|
|
@@ -396,6 +397,38 @@ its own classes all start with `cc-` (`cc-tile`, `cc-icon`, …) and the square'
|
|
|
396
397
|
app's global `.tile`, `.square` or `.place` rules do not reach inside; style it through the
|
|
397
398
|
variables only.
|
|
398
399
|
|
|
400
|
+
## telling us (since 0.6.0)
|
|
401
|
+
|
|
402
|
+
the kit a sheet of the app's own is built on, so anyone using mosaic, cubes or anlat can tell us
|
|
403
|
+
something (a bug, something to make better, an idea, or not known yet) into one pool, the owner's
|
|
404
|
+
todarch, through the backend's `POST /feedback`. headless like the context cube: it says no word and
|
|
405
|
+
draws no sheet. from mosaic's feedback capture (the study *feedback in every cube app*, 4 november
|
|
406
|
+
2026).
|
|
407
|
+
|
|
408
|
+
```ts
|
|
409
|
+
// app.config.ts
|
|
410
|
+
provideHttpClient(withInterceptors([authInterceptor, reportInterceptor])),
|
|
411
|
+
provideReportDiagnostics(),
|
|
412
|
+
{ provide: REPORT_APP, useValue: { id: 'cubes', apiBaseUrl: environment.apiBaseUrl, build: BUILD_INFO } },
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
- `REPORT_APP` (`ReportApp`): the app's id, the backend's address, its build, and optionally
|
|
416
|
+
`appOf(url)`, `screenOf(route, url)` (the app's own words or ids for a place, never a heading or a
|
|
417
|
+
name a person wrote), `environment()`, `draftKey` and `readsReports()` (the owner only).
|
|
418
|
+
- `ReportDiagnostics`, `ReportErrorHandler` (`provideReportDiagnostics()`) and `reportInterceptor`:
|
|
419
|
+
the last 5 errors and failed requests of 10 minutes, a request as its method, path and status (no
|
|
420
|
+
query), an error as its name and 300 characters.
|
|
421
|
+
- `reportContext()` / `ReportClient.context(held)`: app, screen, route pattern, the page's address
|
|
422
|
+
without query or fragment, environment and the held piece's key. `technicalLines` the context note.
|
|
423
|
+
- `ReportClient`: `capture({ kind, text, held })` sends it (and forgets the draft), `request(...)` is
|
|
424
|
+
what it would send, for "what goes with it", `justSaved(text)` a look before sending again
|
|
425
|
+
(`findJustSaved`; only where `readsReports()`: the backend itself answers the same words within 15
|
|
426
|
+
minutes with the same report), `undo(id)`, and `draft` (`ReportDraft`). `reportRefusal(error)` is a
|
|
427
|
+
400's plain reason (20 a day, too long, an app not theirs), shown as it came.
|
|
428
|
+
- `tellLevel(words)`: the part tell, "tell us", the last of every context cube, after hide (selim, 4
|
|
429
|
+
november 2026): something broke, make it better, an idea, not known yet; words in
|
|
430
|
+
`ContextWords.tell`; icons `tell`, `bug`, `improvement`, `idea`, `not-known`.
|
|
431
|
+
|
|
399
432
|
## the square
|
|
400
433
|
|
|
401
434
|
every face of a cube is drawn as a complete square of tiles (decision 23). `<cube-square>` is that
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import * as i0 from "@angular/core";
|
|
2
|
-
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, InjectionToken, PLATFORM_ID, Renderer2, TemplateRef, afterNextRender, computed, contentChild, effect, inject, input, output, signal, untracked, viewChild } from "@angular/core";
|
|
2
|
+
import { ChangeDetectionStrategy, Component, DestroyRef, Directive, ElementRef, ErrorHandler, Injectable, InjectionToken, Injector, PLATFORM_ID, Renderer2, TemplateRef, afterNextRender, computed, contentChild, effect, inject, input, output, runInInjectionContext, signal, untracked, viewChild } from "@angular/core";
|
|
3
3
|
import { NgTemplateOutlet, isPlatformBrowser } from "@angular/common";
|
|
4
|
+
import { HttpClient, HttpErrorResponse } from "@angular/common/http";
|
|
5
|
+
import { catchError, map, of, tap } from "rxjs";
|
|
6
|
+
import { Router } from "@angular/router";
|
|
4
7
|
function sizeFor(count) {
|
|
5
8
|
let s = 1;
|
|
6
9
|
while (s * s * s < count && s < 7) s++;
|
|
@@ -1099,6 +1102,11 @@ const CUBE_ICON_NAMES = [
|
|
|
1099
1102
|
"up",
|
|
1100
1103
|
"down",
|
|
1101
1104
|
"plus",
|
|
1105
|
+
"tell",
|
|
1106
|
+
"bug",
|
|
1107
|
+
"improvement",
|
|
1108
|
+
"idea",
|
|
1109
|
+
"not-known",
|
|
1102
1110
|
"square",
|
|
1103
1111
|
"one",
|
|
1104
1112
|
"two",
|
|
@@ -1276,6 +1284,24 @@ var CubeIcon = class CubeIcon {
|
|
|
1276
1284
|
<path d="M3 12s3.2-6 9-6 9 6 9 6-3.2 6-9 6-9-6-9-6Z" />
|
|
1277
1285
|
<circle cx="12" cy="12" r="2.6" />
|
|
1278
1286
|
}
|
|
1287
|
+
@case ('tell') {
|
|
1288
|
+
<path d="M5 4.5h14a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-7l-4.5 3.6v-3.6H5a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2Z" />
|
|
1289
|
+
}
|
|
1290
|
+
@case ('bug') {
|
|
1291
|
+
<path d="M5 5h14v14H5Z" />
|
|
1292
|
+
<path d="M12.5 5 10 9.5l3.5 3L11 19" />
|
|
1293
|
+
}
|
|
1294
|
+
@case ('improvement') {
|
|
1295
|
+
<path d="M4 17.5 9.5 12l3.5 3L20 8" />
|
|
1296
|
+
<path d="M15 8h5v5" />
|
|
1297
|
+
}
|
|
1298
|
+
@case ('idea') {
|
|
1299
|
+
<path d="M9.5 17.5h5M10.5 20.5h3" />
|
|
1300
|
+
<path d="M12 3.5a6 6 0 0 0-3.6 10.8c.7.5 1.1 1.3 1.1 2.1v.1h5v-.1c0-.8.4-1.6 1.1-2.1A6 6 0 0 0 12 3.5Z" />
|
|
1301
|
+
}
|
|
1302
|
+
@case ('not-known') {
|
|
1303
|
+
<circle cx="12" cy="12" r="8" stroke-dasharray="2.6 2.6" />
|
|
1304
|
+
}
|
|
1279
1305
|
@case ('hidden') {
|
|
1280
1306
|
<path d="M3 12s3.2-6 9-6c1.6 0 3 .45 4.2 1.1M21 12s-3.2 6-9 6c-1.6 0-3-.45-4.2-1.1" />
|
|
1281
1307
|
<path d="M4.5 19.5 19.5 4.5" />
|
|
@@ -1383,6 +1409,24 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
1383
1409
|
<path d="M3 12s3.2-6 9-6 9 6 9 6-3.2 6-9 6-9-6-9-6Z" />
|
|
1384
1410
|
<circle cx="12" cy="12" r="2.6" />
|
|
1385
1411
|
}
|
|
1412
|
+
@case ('tell') {
|
|
1413
|
+
<path d="M5 4.5h14a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2h-7l-4.5 3.6v-3.6H5a2 2 0 0 1-2-2v-8a2 2 0 0 1 2-2Z" />
|
|
1414
|
+
}
|
|
1415
|
+
@case ('bug') {
|
|
1416
|
+
<path d="M5 5h14v14H5Z" />
|
|
1417
|
+
<path d="M12.5 5 10 9.5l3.5 3L11 19" />
|
|
1418
|
+
}
|
|
1419
|
+
@case ('improvement') {
|
|
1420
|
+
<path d="M4 17.5 9.5 12l3.5 3L20 8" />
|
|
1421
|
+
<path d="M15 8h5v5" />
|
|
1422
|
+
}
|
|
1423
|
+
@case ('idea') {
|
|
1424
|
+
<path d="M9.5 17.5h5M10.5 20.5h3" />
|
|
1425
|
+
<path d="M12 3.5a6 6 0 0 0-3.6 10.8c.7.5 1.1 1.3 1.1 2.1v.1h5v-.1c0-.8.4-1.6 1.1-2.1A6 6 0 0 0 12 3.5Z" />
|
|
1426
|
+
}
|
|
1427
|
+
@case ('not-known') {
|
|
1428
|
+
<circle cx="12" cy="12" r="8" stroke-dasharray="2.6 2.6" />
|
|
1429
|
+
}
|
|
1386
1430
|
@case ('hidden') {
|
|
1387
1431
|
<path d="M3 12s3.2-6 9-6c1.6 0 3 .45 4.2 1.1M21 12s-3.2 6-9 6c-1.6 0-3-.45-4.2-1.1" />
|
|
1388
1432
|
<path d="M4.5 19.5 19.5 4.5" />
|
|
@@ -1424,11 +1468,20 @@ const CONTEXT_PART_ORDER = [
|
|
|
1424
1468
|
"move",
|
|
1425
1469
|
"where",
|
|
1426
1470
|
"whose",
|
|
1427
|
-
"hide"
|
|
1471
|
+
"hide",
|
|
1472
|
+
"tell"
|
|
1428
1473
|
];
|
|
1474
|
+
const CONTEXT_LAST_PARTS = ["tell"];
|
|
1429
1475
|
function orderParts(parts) {
|
|
1430
1476
|
const unique = [...new Set(parts)];
|
|
1431
|
-
|
|
1477
|
+
const known = CONTEXT_PART_ORDER.filter((p) => unique.includes(p) && !CONTEXT_LAST_PARTS.includes(p));
|
|
1478
|
+
const own = unique.filter((p) => !CONTEXT_PART_ORDER.includes(p));
|
|
1479
|
+
const last = CONTEXT_LAST_PARTS.filter((p) => unique.includes(p));
|
|
1480
|
+
return [
|
|
1481
|
+
...known,
|
|
1482
|
+
...own,
|
|
1483
|
+
...last
|
|
1484
|
+
];
|
|
1432
1485
|
}
|
|
1433
1486
|
const CONTEXT_CUBE_SOURCE = new InjectionToken("CONTEXT_CUBE_SOURCE");
|
|
1434
1487
|
const CONTEXT_CUBE_WORDS = new InjectionToken("CONTEXT_CUBE_WORDS");
|
|
@@ -1646,6 +1699,22 @@ function addLevel(ways) {
|
|
|
1646
1699
|
});
|
|
1647
1700
|
return [...ways.filter((w) => !w.adds), ...ways.filter((w) => w.adds)].map(tile);
|
|
1648
1701
|
}
|
|
1702
|
+
function tellLevel(words) {
|
|
1703
|
+
const w = words.tell;
|
|
1704
|
+
const tile = (key, data, word, icon) => ({
|
|
1705
|
+
key: "tell:" + key,
|
|
1706
|
+
title: word?.title ?? "",
|
|
1707
|
+
line: word?.line ?? null,
|
|
1708
|
+
icon,
|
|
1709
|
+
data
|
|
1710
|
+
});
|
|
1711
|
+
return [
|
|
1712
|
+
tile("bug", "bug", w?.bug, "bug"),
|
|
1713
|
+
tile("improvement", "improvement", w?.improvement, "improvement"),
|
|
1714
|
+
tile("idea", "idea", w?.idea, "idea"),
|
|
1715
|
+
tile("not-known", null, w?.notKnown, "not-known")
|
|
1716
|
+
];
|
|
1717
|
+
}
|
|
1649
1718
|
function insideOf(source, piece) {
|
|
1650
1719
|
const seen = /* @__PURE__ */ new Set();
|
|
1651
1720
|
const walk = [...source.partsOf(piece)];
|
|
@@ -3424,6 +3493,265 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3424
3493
|
}]
|
|
3425
3494
|
}
|
|
3426
3495
|
});
|
|
3427
|
-
|
|
3496
|
+
const REPORT_KINDS = [
|
|
3497
|
+
"bug",
|
|
3498
|
+
"improvement",
|
|
3499
|
+
"idea"
|
|
3500
|
+
];
|
|
3501
|
+
const REPORT_APP = new InjectionToken("REPORT_APP");
|
|
3502
|
+
const REPORT_INCIDENTS_KEPT = 5;
|
|
3503
|
+
const REPORT_INCIDENTS_MS = 6e5;
|
|
3504
|
+
const ERROR_CHARS = 300;
|
|
3505
|
+
function scrubbedPath(url) {
|
|
3506
|
+
return url.replace(/^[a-z][a-z0-9+.-]*:\/\/[^/]+/i, "").replace(/[?#].*$/, "") || "/";
|
|
3507
|
+
}
|
|
3508
|
+
function errorLine(error) {
|
|
3509
|
+
return `error: ${(error instanceof Error ? `${error.name}: ${error.message}` : String(error ?? "unknown error")).slice(0, ERROR_CHARS)}`;
|
|
3510
|
+
}
|
|
3511
|
+
var ReportDiagnostics = class ReportDiagnostics {
|
|
3512
|
+
incidents = [];
|
|
3513
|
+
constructor() {
|
|
3514
|
+
if (typeof window === "undefined") return;
|
|
3515
|
+
window.addEventListener("error", (e) => this.recordError(e.error ?? e.message));
|
|
3516
|
+
window.addEventListener("unhandledrejection", (e) => this.recordError(e.reason));
|
|
3517
|
+
}
|
|
3518
|
+
recordError(error) {
|
|
3519
|
+
this.add(errorLine(error));
|
|
3520
|
+
}
|
|
3521
|
+
recordFailedRequest(method, url, status) {
|
|
3522
|
+
this.add(`${method} ${scrubbedPath(url)} -> ${status || "no response"}`);
|
|
3523
|
+
}
|
|
3524
|
+
recent(now = Date.now()) {
|
|
3525
|
+
const since = now - REPORT_INCIDENTS_MS;
|
|
3526
|
+
return this.incidents.filter((i) => i.at >= since).map((i) => i.text);
|
|
3527
|
+
}
|
|
3528
|
+
add(text) {
|
|
3529
|
+
this.incidents = [...this.incidents, {
|
|
3530
|
+
at: Date.now(),
|
|
3531
|
+
text
|
|
3532
|
+
}].slice(-5);
|
|
3533
|
+
}
|
|
3534
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
3535
|
+
minVersion: "12.0.0",
|
|
3536
|
+
version: "22.2.4",
|
|
3537
|
+
ngImport: i0,
|
|
3538
|
+
type: ReportDiagnostics,
|
|
3539
|
+
deps: [],
|
|
3540
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
3541
|
+
});
|
|
3542
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
3543
|
+
minVersion: "12.0.0",
|
|
3544
|
+
version: "22.2.4",
|
|
3545
|
+
ngImport: i0,
|
|
3546
|
+
type: ReportDiagnostics,
|
|
3547
|
+
providedIn: "root"
|
|
3548
|
+
});
|
|
3549
|
+
};
|
|
3550
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
3551
|
+
minVersion: "12.0.0",
|
|
3552
|
+
version: "22.2.4",
|
|
3553
|
+
ngImport: i0,
|
|
3554
|
+
type: ReportDiagnostics,
|
|
3555
|
+
decorators: [{
|
|
3556
|
+
type: Injectable,
|
|
3557
|
+
args: [{ providedIn: "root" }]
|
|
3558
|
+
}],
|
|
3559
|
+
ctorParameters: () => []
|
|
3560
|
+
});
|
|
3561
|
+
var ReportErrorHandler = class ReportErrorHandler extends ErrorHandler {
|
|
3562
|
+
diagnostics = inject(ReportDiagnostics);
|
|
3563
|
+
handleError(error) {
|
|
3564
|
+
this.diagnostics.recordError(error);
|
|
3565
|
+
super.handleError(error);
|
|
3566
|
+
}
|
|
3567
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
3568
|
+
minVersion: "12.0.0",
|
|
3569
|
+
version: "22.2.4",
|
|
3570
|
+
ngImport: i0,
|
|
3571
|
+
type: ReportErrorHandler,
|
|
3572
|
+
deps: null,
|
|
3573
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
3574
|
+
});
|
|
3575
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
3576
|
+
minVersion: "12.0.0",
|
|
3577
|
+
version: "22.2.4",
|
|
3578
|
+
ngImport: i0,
|
|
3579
|
+
type: ReportErrorHandler
|
|
3580
|
+
});
|
|
3581
|
+
};
|
|
3582
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
3583
|
+
minVersion: "12.0.0",
|
|
3584
|
+
version: "22.2.4",
|
|
3585
|
+
ngImport: i0,
|
|
3586
|
+
type: ReportErrorHandler,
|
|
3587
|
+
decorators: [{ type: Injectable }]
|
|
3588
|
+
});
|
|
3589
|
+
const reportInterceptor = (req, next) => {
|
|
3590
|
+
const diagnostics = inject(ReportDiagnostics);
|
|
3591
|
+
return next(req).pipe(tap({ error: (error) => {
|
|
3592
|
+
if (error instanceof HttpErrorResponse && !/\/feedback(\/|$|\?)/.test(req.url)) diagnostics.recordFailedRequest(req.method, req.url, error.status);
|
|
3593
|
+
} }));
|
|
3594
|
+
};
|
|
3595
|
+
function provideReportDiagnostics() {
|
|
3596
|
+
return [{
|
|
3597
|
+
provide: ErrorHandler,
|
|
3598
|
+
useClass: ReportErrorHandler
|
|
3599
|
+
}];
|
|
3600
|
+
}
|
|
3601
|
+
function routePattern(root) {
|
|
3602
|
+
const parts = [];
|
|
3603
|
+
let route = root;
|
|
3604
|
+
while (route) {
|
|
3605
|
+
const path = route.routeConfig?.path;
|
|
3606
|
+
if (path) parts.push(path);
|
|
3607
|
+
route = route.firstChild;
|
|
3608
|
+
}
|
|
3609
|
+
return parts.join("/");
|
|
3610
|
+
}
|
|
3611
|
+
function environmentOf(host) {
|
|
3612
|
+
if (/^(localhost|127\.0\.0\.1|\[::1\])(:|$)/.test(host)) return "local";
|
|
3613
|
+
if (/^dev(--|\.)/.test(host)) return "dev";
|
|
3614
|
+
return "prod";
|
|
3615
|
+
}
|
|
3616
|
+
function contextOf(app, at, held = null) {
|
|
3617
|
+
const path = at.url.replace(/[?#].*$/, "") || "/";
|
|
3618
|
+
return {
|
|
3619
|
+
app: app.appOf?.(path) ?? app.id,
|
|
3620
|
+
screen: app.screenOf?.(at.route, path) ?? (at.route || "/"),
|
|
3621
|
+
route: at.route,
|
|
3622
|
+
pageUrl: at.origin + (at.pathname ?? path),
|
|
3623
|
+
environment: app.environment?.() ?? environmentOf(at.host),
|
|
3624
|
+
held
|
|
3625
|
+
};
|
|
3626
|
+
}
|
|
3627
|
+
function reportContext(opts = {}) {
|
|
3628
|
+
const app = inject(REPORT_APP);
|
|
3629
|
+
const router = inject(Router);
|
|
3630
|
+
return contextOf(app, {
|
|
3631
|
+
url: router.url,
|
|
3632
|
+
route: routePattern(router.routerState.snapshot.root),
|
|
3633
|
+
origin: typeof location === "undefined" ? "" : location.origin,
|
|
3634
|
+
host: typeof location === "undefined" ? "" : location.host,
|
|
3635
|
+
pathname: typeof location === "undefined" ? void 0 : location.pathname
|
|
3636
|
+
}, opts.held ?? null);
|
|
3637
|
+
}
|
|
3638
|
+
function technicalLines(input) {
|
|
3639
|
+
const lines = [];
|
|
3640
|
+
if (input.held) lines.push(`held ${input.held}`);
|
|
3641
|
+
const b = input.build;
|
|
3642
|
+
if (b?.commitShort) lines.push(`build ${b.commitShort} (${b.branch ?? "?"}, ${b.builtAt ?? "?"})`);
|
|
3643
|
+
if (input.size) lines.push(`screen ${input.size.width}x${input.size.height}${input.agent ? ", " + input.agent : ""}`);
|
|
3644
|
+
return [...lines, ...input.incidents ?? []];
|
|
3645
|
+
}
|
|
3646
|
+
const JUST_SAVED_MINUTES = 15;
|
|
3647
|
+
function findJustSaved(reports, detail, now = /* @__PURE__ */ new Date()) {
|
|
3648
|
+
const text = detail.trim();
|
|
3649
|
+
const since = now.getTime() - 9e5;
|
|
3650
|
+
return reports.find((r) => (r.detail ?? "").trim() === text && !!r.reported && new Date(r.reported).getTime() >= since);
|
|
3651
|
+
}
|
|
3652
|
+
var ReportDraft = class {
|
|
3653
|
+
key;
|
|
3654
|
+
constructor(key) {
|
|
3655
|
+
this.key = key;
|
|
3656
|
+
}
|
|
3657
|
+
read() {
|
|
3658
|
+
try {
|
|
3659
|
+
return localStorage.getItem(this.key) ?? "";
|
|
3660
|
+
} catch {
|
|
3661
|
+
return "";
|
|
3662
|
+
}
|
|
3663
|
+
}
|
|
3664
|
+
write(text) {
|
|
3665
|
+
try {
|
|
3666
|
+
if (text.trim()) localStorage.setItem(this.key, text);
|
|
3667
|
+
else localStorage.removeItem(this.key);
|
|
3668
|
+
} catch {}
|
|
3669
|
+
}
|
|
3670
|
+
clear() {
|
|
3671
|
+
this.write("");
|
|
3672
|
+
}
|
|
3673
|
+
};
|
|
3674
|
+
function reportRefusal(error) {
|
|
3675
|
+
if (!(error instanceof HttpErrorResponse) || error.status !== 400) return null;
|
|
3676
|
+
const body = error.error;
|
|
3677
|
+
if (typeof body === "string") return body.trim() || null;
|
|
3678
|
+
const said = body?.message;
|
|
3679
|
+
return typeof said === "string" && said.trim() ? said.trim() : null;
|
|
3680
|
+
}
|
|
3681
|
+
function reportRequest(input, ctx, technical) {
|
|
3682
|
+
return {
|
|
3683
|
+
...input.kind ? { type: input.kind } : {},
|
|
3684
|
+
detail: input.text.trim(),
|
|
3685
|
+
app: ctx.app,
|
|
3686
|
+
screen: ctx.screen,
|
|
3687
|
+
pageUrl: ctx.pageUrl,
|
|
3688
|
+
route: ctx.route,
|
|
3689
|
+
environment: ctx.environment,
|
|
3690
|
+
context: technical.join("\n")
|
|
3691
|
+
};
|
|
3692
|
+
}
|
|
3693
|
+
var ReportClient = class ReportClient {
|
|
3694
|
+
http = inject(HttpClient);
|
|
3695
|
+
app = inject(REPORT_APP);
|
|
3696
|
+
diagnostics = inject(ReportDiagnostics);
|
|
3697
|
+
injector = inject(Injector);
|
|
3698
|
+
url = `${this.app.apiBaseUrl.replace(/\/$/, "")}/feedback`;
|
|
3699
|
+
draft = new ReportDraft(this.app.draftKey ?? `${this.app.id}.report.draft`);
|
|
3700
|
+
context(held = null) {
|
|
3701
|
+
return runInInjectionContext(this.injector, () => reportContext({ held }));
|
|
3702
|
+
}
|
|
3703
|
+
incidents() {
|
|
3704
|
+
return this.diagnostics.recent();
|
|
3705
|
+
}
|
|
3706
|
+
request(input, ctx = this.context(input.held ?? null)) {
|
|
3707
|
+
const withIncidents = input.withIncidents ?? input.kind === "bug";
|
|
3708
|
+
return reportRequest(input, ctx, technicalLines({
|
|
3709
|
+
held: input.held ?? ctx.held,
|
|
3710
|
+
build: this.app.build,
|
|
3711
|
+
size: typeof window === "undefined" ? null : {
|
|
3712
|
+
width: window.innerWidth,
|
|
3713
|
+
height: window.innerHeight
|
|
3714
|
+
},
|
|
3715
|
+
agent: typeof navigator === "undefined" ? null : navigator.userAgent,
|
|
3716
|
+
incidents: withIncidents ? this.incidents() : []
|
|
3717
|
+
}));
|
|
3718
|
+
}
|
|
3719
|
+
capture(input, ctx) {
|
|
3720
|
+
return this.http.post(this.url, this.request(input, ctx)).pipe(map((saved) => (this.draft.clear(), saved)));
|
|
3721
|
+
}
|
|
3722
|
+
justSaved(text) {
|
|
3723
|
+
if (!this.app.readsReports?.()) return of(null);
|
|
3724
|
+
return this.http.get(this.url).pipe(map((reports) => findJustSaved(reports, text) ?? null), catchError(() => of(null)));
|
|
3725
|
+
}
|
|
3726
|
+
undo(id) {
|
|
3727
|
+
return this.http.delete(`${this.url}/${encodeURIComponent(id)}`);
|
|
3728
|
+
}
|
|
3729
|
+
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
3730
|
+
minVersion: "12.0.0",
|
|
3731
|
+
version: "22.2.4",
|
|
3732
|
+
ngImport: i0,
|
|
3733
|
+
type: ReportClient,
|
|
3734
|
+
deps: [],
|
|
3735
|
+
target: i0.ɵɵFactoryTarget.Injectable
|
|
3736
|
+
});
|
|
3737
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({
|
|
3738
|
+
minVersion: "12.0.0",
|
|
3739
|
+
version: "22.2.4",
|
|
3740
|
+
ngImport: i0,
|
|
3741
|
+
type: ReportClient,
|
|
3742
|
+
providedIn: "root"
|
|
3743
|
+
});
|
|
3744
|
+
};
|
|
3745
|
+
i0.ɵɵngDeclareClassMetadata({
|
|
3746
|
+
minVersion: "12.0.0",
|
|
3747
|
+
version: "22.2.4",
|
|
3748
|
+
ngImport: i0,
|
|
3749
|
+
type: ReportClient,
|
|
3750
|
+
decorators: [{
|
|
3751
|
+
type: Injectable,
|
|
3752
|
+
args: [{ providedIn: "root" }]
|
|
3753
|
+
}]
|
|
3754
|
+
});
|
|
3755
|
+
export { CONTEXT_CUBE_SOURCE, CONTEXT_CUBE_WORDS, CONTEXT_LAST_PARTS, CONTEXT_PART_ORDER, CUBE_CARRY_CLASSES, CUBE_CARRY_MOVE, CUBE_DROP_OUT, CUBE_HOLD_MS, CUBE_HOLD_SLOP, CUBE_ICON_NAMES, CUBE_LOADING_COLOURS, CUBE_LOADING_LEVELS, CUBE_LOADING_STEP_MS, CUBE_LOADING_STICKERS, CUBE_LOADING_WAYS, CUBE_LOADING_WIDTH, CUBE_PINCH_APART, CUBE_PINCH_TOGETHER, CUBE_WHEEL_QUIET_MS, CubeCarry, CubeContext, CubeHold, CubeIcon, CubeLoading, CubePicture, CubePinch, CubeSquare, CubeSquareTile, JUST_SAVED_MINUTES, MINE_SORT, PART_ICONS, REPORT_APP, REPORT_INCIDENTS_KEPT, REPORT_INCIDENTS_MS, REPORT_KINDS, ReportClient, ReportDiagnostics, ReportDraft, ReportErrorHandler, SQUARE_LEAST_CELL, SQUARE_LIST_ROW, SQUARE_MAX_SIDE, TEXT_ORDERS, TURNING_BELOW, TURNING_NUDGE_UNTIL, TURNING_SIDES, VIEW_KEYS, addLevel, bringLevel, bringOf, contextIcon, contextLevelFor, contextOf, contextSquare, cubeIconCells, directionWords, drawCube, environmentOf, errorLine, findJustSaved, gridFor, hideLevel, insideOf, isView, loadingStickers, loadingWayFor, moveBefore, namesLevel, orderLevel, orderOf, orderParts, partsLevel, placesOf, provideReportDiagnostics, refusalOf, reportContext, reportInterceptor, reportRefusal, reportRequest, routePattern, scrubbedPath, sizeFor, sortDirectionsLevel, sortItems, sortLevel, squareGap, squareLayout, squareLeast, squareLevelFor, startsWheelGesture, stillStep, technicalLines, tellLevel, turningFront, turningRows, viewLevel, whereLevel, wouldLoop };
|
|
3428
3756
|
|
|
3429
3757
|
//# sourceMappingURL=selimssevgi-cube-design-angular.mjs.map
|