@selimssevgi/cube-design-angular 0.5.0 → 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,16 @@
|
|
|
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
|
+
|
|
13
|
+
## 0.5.1 (4 november 2026: an empty piece has room)
|
|
14
|
+
|
|
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.
|
|
16
|
+
|
|
7
17
|
## 0.5.0 (4 november 2026: released so mosaic's home grouped my way brings things in and keeps my own order)
|
|
8
18
|
|
|
9
19
|
- **bring things in**, `bringOf(source, into, { at, pieces })` and `bringLevel` (`CubeBring`, `BringOptions`, `BringWords`): where turned round (the study `studies/bringing-things-in.md`, way b, picked by selim on 4 november 2026). where lists places for one piece; bring lists pieces for one place: what stands in `at` (the top by default), each ticked when it is in already, the place itself and a piece holding it pale, a › on a piece with something to bring inside it, what a tap asks (`stand`, `take-out`) and why it would be refused, over the same `WhereSource`. `pieces` lists the app's own (lately) instead. from mosaic's grouping prototype (`bring-things-in.html`).
|
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)];
|
|
@@ -1853,11 +1922,18 @@ function squareGap(n) {
|
|
|
1853
1922
|
}
|
|
1854
1923
|
const SQUARE_MAX_SIDE = 880;
|
|
1855
1924
|
const SQUARE_LIST_ROW = 76;
|
|
1925
|
+
const SQUARE_LEAST_CELL = 200;
|
|
1926
|
+
function squareLeast(side, opts = {}) {
|
|
1927
|
+
const { cell = 200, min = 3, max = 5 } = opts;
|
|
1928
|
+
if (!(side > 0)) return min;
|
|
1929
|
+
return Math.min(max, Math.max(min, Math.ceil(side / cell)));
|
|
1930
|
+
}
|
|
1856
1931
|
function squareLayout(view, count, room, opts = {}) {
|
|
1857
1932
|
const n = Math.max(0, count);
|
|
1858
1933
|
const grid = gridFor(view, n);
|
|
1859
|
-
const
|
|
1860
|
-
const
|
|
1934
|
+
const least = view === "square" ? Math.max(0, Math.floor(opts.least ?? 0)) : 0;
|
|
1935
|
+
const cols = Math.max(grid.cols, least);
|
|
1936
|
+
const rows = view === "square" ? Math.max(grid.rows, least) : Math.max(1, Math.ceil(n / cols));
|
|
1861
1937
|
const width = Math.max(0, Math.min(view === "square" ? Math.min(room.width, room.tall) : room.width, opts.maxSide ?? 880));
|
|
1862
1938
|
const list = view === "one";
|
|
1863
1939
|
const gap = list ? 8 : squareGap(Math.max(cols, Math.min(rows, 5)));
|
|
@@ -1964,6 +2040,7 @@ var CubeSquare = class CubeSquare {
|
|
|
1964
2040
|
maxSide = input(880, ...ngDevMode ? [{ debugName: "maxSide" }] : /* istanbul ignore next */ []);
|
|
1965
2041
|
listRow = input(76, ...ngDevMode ? [{ debugName: "listRow" }] : /* istanbul ignore next */ []);
|
|
1966
2042
|
dashed = input(false, ...ngDevMode ? [{ debugName: "dashed" }] : /* istanbul ignore next */ []);
|
|
2043
|
+
least = input("room", ...ngDevMode ? [{ debugName: "least" }] : /* istanbul ignore next */ []);
|
|
1967
2044
|
keyOf = input((_, i) => i, ...ngDevMode ? [{ debugName: "keyOf" }] : /* istanbul ignore next */ []);
|
|
1968
2045
|
turning = input(false, ...ngDevMode ? [{ debugName: "turning" }] : /* istanbul ignore next */ []);
|
|
1969
2046
|
turnBelow = input(640, ...ngDevMode ? [{ debugName: "turnBelow" }] : /* istanbul ignore next */ []);
|
|
@@ -1989,9 +2066,12 @@ var CubeSquare = class CubeSquare {
|
|
|
1989
2066
|
width,
|
|
1990
2067
|
tall: r.tall || width
|
|
1991
2068
|
};
|
|
2069
|
+
const least = this.least();
|
|
2070
|
+
const side = Math.min(width, room.tall, this.maxSide());
|
|
1992
2071
|
const opts = {
|
|
1993
2072
|
maxSide: this.maxSide(),
|
|
1994
|
-
listRow: this.listRow()
|
|
2073
|
+
listRow: this.listRow(),
|
|
2074
|
+
least: !this.dashed() ? 0 : least === "room" ? squareLeast(side) : least
|
|
1995
2075
|
};
|
|
1996
2076
|
const front = this.front();
|
|
1997
2077
|
if (!front) return squareLayout(this.view(), this.tiles().length, room, opts);
|
|
@@ -2246,6 +2326,13 @@ var CubeSquare = class CubeSquare {
|
|
|
2246
2326
|
isRequired: false,
|
|
2247
2327
|
transformFunction: null
|
|
2248
2328
|
},
|
|
2329
|
+
least: {
|
|
2330
|
+
classPropertyName: "least",
|
|
2331
|
+
publicName: "least",
|
|
2332
|
+
isSignal: true,
|
|
2333
|
+
isRequired: false,
|
|
2334
|
+
transformFunction: null
|
|
2335
|
+
},
|
|
2249
2336
|
keyOf: {
|
|
2250
2337
|
classPropertyName: "keyOf",
|
|
2251
2338
|
publicName: "keyOf",
|
|
@@ -2591,6 +2678,14 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
2591
2678
|
required: false
|
|
2592
2679
|
}]
|
|
2593
2680
|
}],
|
|
2681
|
+
least: [{
|
|
2682
|
+
type: i0.Input,
|
|
2683
|
+
args: [{
|
|
2684
|
+
isSignal: true,
|
|
2685
|
+
alias: "least",
|
|
2686
|
+
required: false
|
|
2687
|
+
}]
|
|
2688
|
+
}],
|
|
2594
2689
|
keyOf: [{
|
|
2595
2690
|
type: i0.Input,
|
|
2596
2691
|
args: [{
|
|
@@ -2946,6 +3041,7 @@ var CubeContext = class CubeContext {
|
|
|
2946
3041
|
"maxSide",
|
|
2947
3042
|
"listRow",
|
|
2948
3043
|
"dashed",
|
|
3044
|
+
"least",
|
|
2949
3045
|
"keyOf",
|
|
2950
3046
|
"turning",
|
|
2951
3047
|
"turnBelow",
|
|
@@ -3289,6 +3385,7 @@ var CubeLoading = class CubeLoading {
|
|
|
3289
3385
|
"maxSide",
|
|
3290
3386
|
"listRow",
|
|
3291
3387
|
"dashed",
|
|
3388
|
+
"least",
|
|
3292
3389
|
"keyOf",
|
|
3293
3390
|
"turning",
|
|
3294
3391
|
"turnBelow",
|
|
@@ -3396,6 +3493,265 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
3396
3493
|
}]
|
|
3397
3494
|
}
|
|
3398
3495
|
});
|
|
3399
|
-
|
|
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 };
|
|
3400
3756
|
|
|
3401
3757
|
//# sourceMappingURL=selimssevgi-cube-design-angular.mjs.map
|