@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@selimssevgi/cube-design-angular",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "cube design in angular: the pieces anlat, mosaic and cubes share, starting with the isometric cube picture.",
5
5
  "keywords": [
6
6
  "angular",
@@ -15,7 +15,8 @@
15
15
  },
16
16
  "peerDependencies": {
17
17
  "@angular/common": "^22.2.0",
18
- "@angular/core": "^22.2.0"
18
+ "@angular/core": "^22.2.0",
19
+ "@angular/router": "^22.2.0"
19
20
  },
20
21
  "dependencies": {
21
22
  "tslib": "^2.3.0"
@@ -1,5 +1,8 @@
1
1
  import * as i0 from "@angular/core";
2
- import { InjectionToken, TemplateRef } from "@angular/core";
2
+ import { ErrorHandler, InjectionToken, Provider, TemplateRef } from "@angular/core";
3
+ import { HttpInterceptorFn } from "@angular/common/http";
4
+ import { ActivatedRouteSnapshot } from "@angular/router";
5
+ import { Observable } from "rxjs";
3
6
  /**
4
7
  * drawing a cube of small cubes in isometric view: the left, right and top faces, each split into
5
8
  * n × n stickers. a sticker is coloured by a piece, or pale while nothing is known for it.
@@ -396,7 +399,7 @@ export declare class CubeCarry {
396
399
  /** the views a cube can be shown in, each drawn as its own shape. */
397
400
  export type CubeIconView = 'square' | 'one' | 'two' | 'three' | 'four' | 'grid-4x5' | 'grid-3x6';
398
401
  /** what a tile of a cube the app keeps is about (a context cube's parts and their choices). */
399
- export type CubeIconName = 'names' | 'name' | 'change' | 'move' | 'place' | 'whose' | 'library' | 'mine' | 'shown' | 'hidden' | 'view' | 'sort' | 'kept' | 'face' | 'added' | 'up' | 'down' | 'plus' | CubeIconView;
402
+ export type CubeIconName = 'names' | 'name' | 'change' | 'move' | 'place' | 'whose' | 'library' | 'mine' | 'shown' | 'hidden' | 'view' | 'sort' | 'kept' | 'face' | 'added' | 'up' | 'down' | 'plus' | 'tell' | 'bug' | 'improvement' | 'idea' | 'not-known' | CubeIconView;
400
403
  /** every icon, in the order above. */
401
404
  export declare const CUBE_ICON_NAMES: readonly CubeIconName[];
402
405
  export interface CubeIconCell {
@@ -657,16 +660,24 @@ export declare function bringLevel(pieces: readonly CubeBring[], words?: BringWo
657
660
  }): ContextTile<CubeBring>[];
658
661
  /**
659
662
  * the parts a context cube can have. the seven are the ones cubes found, where (one piece in many
660
- * places, 0.3.0), and bring and order (bringing things in and my own order, 0.5.0); an app may add
661
- * its own, any string, and they come after the known ones.
663
+ * places, 0.3.0), bring and order (bringing things in and my own order, 0.5.0) and tell (telling us
664
+ * something about it, 0.6.0); an app may add its own, any string, and they come after the known
665
+ * ones, before tell.
662
666
  */
663
- export type ContextPartKey = 'names' | 'change' | 'bring' | 'view' | 'sort' | 'order' | 'move' | 'where' | 'whose' | 'hide' | (string & {});
667
+ export type ContextPartKey = 'names' | 'change' | 'bring' | 'view' | 'sort' | 'order' | 'move' | 'where' | 'whose' | 'hide' | 'tell' | (string & {});
664
668
  /**
665
669
  * cubes' rows: how it is called (names, change, and bring things into it), how it is shown (view,
666
- * sort, my own order), what i do with it (move, where, whose, hide).
670
+ * sort, my own order), what i do with it (move, where, whose, hide), and last what i tell about it
671
+ * (tell; selim's decision of 4 november 2026: "tell us" is the last part of every context cube,
672
+ * after hide).
667
673
  */
668
674
  export declare const CONTEXT_PART_ORDER: readonly ContextPartKey[];
669
- /** the parts in their rows; parts an app added come last, in the order it gave them. twice is once. */
675
+ /** the parts that stay last, after the parts an app added: tell. */
676
+ export declare const CONTEXT_LAST_PARTS: readonly ContextPartKey[];
677
+ /**
678
+ * the parts in their rows; parts an app added come after the known ones, in the order it gave
679
+ * them, and tell stays last. twice is once.
680
+ */
670
681
  export declare function orderParts(parts: readonly ContextPartKey[]): ContextPartKey[];
671
682
  /** one tile of a level. its title and line always come from the app. */
672
683
  export interface ContextTile<D = unknown> {
@@ -760,6 +771,18 @@ export interface ContextWords {
760
771
  inUse?: string;
761
772
  /** where's title and line for each place (`whereLevel`); the place's name and no line without. */
762
773
  where?: WhereWords;
774
+ /**
775
+ * tell's four pieces (`tellLevel`): something broke, make it better, an idea, and not known yet
776
+ * (the person says it without choosing). an app without them shows tell with empty titles.
777
+ */
778
+ tell?: TellWords;
779
+ }
780
+ /** the words of tell's level, one for each kind of report and one for not known yet. */
781
+ export interface TellWords {
782
+ bug: ContextWord;
783
+ improvement: ContextWord;
784
+ idea: ContextWord;
785
+ notKnown: ContextWord;
763
786
  }
764
787
  /** the app's source of context cubes. */
765
788
  export declare const CONTEXT_CUBE_SOURCE: InjectionToken<ContextCubeSource<any>>;
@@ -779,6 +802,46 @@ export interface PartNow {
779
802
  export declare const PART_ICONS: Partial<Record<ContextPartKey, string>>;
780
803
  /** the top of a context cube: its parts in rows, each with its word and, when given, what it holds now. */
781
804
  export declare function partsLevel(parts: readonly ContextPartKey[], words: ContextWords, now?: Partial<Record<ContextPartKey, PartNow>>): ContextTile<ContextPartKey>[];
805
+ /** what a report is about. a report may also be not known yet (null): the person did not say. */
806
+ export type ReportKind = 'bug' | 'improvement' | 'idea';
807
+ /** every kind, in the order a sheet offers them. */
808
+ export declare const REPORT_KINDS: readonly ReportKind[];
809
+ /** when and from what the app was built, as its `gen-build-info.mjs` writes it. */
810
+ export interface ReportBuild {
811
+ commitShort?: string | null;
812
+ branch?: string | null;
813
+ builtAt?: string | null;
814
+ }
815
+ /** what the app tells the kit about itself, through `REPORT_APP`. */
816
+ export interface ReportApp {
817
+ /** the app's id, sent as `app`: `cubes`, `anlat`; mosaic's sub-apps through `appOf`. */
818
+ id: string;
819
+ /** the backend's address, without `/feedback` (each app's `environment.apiBaseUrl`). */
820
+ apiBaseUrl: string;
821
+ /** the build, for the technical lines. */
822
+ build?: ReportBuild | null;
823
+ /**
824
+ * the app id for an address, when one app hosts several (mosaic: its sub-app, else `general`);
825
+ * `id` when not given or when it answers null.
826
+ */
827
+ appOf?: (url: string) => string | null;
828
+ /**
829
+ * the app's own words for a screen, from its route pattern and address: an id or the app's own
830
+ * name for that place, never a heading or a name a person wrote. the route pattern when not given.
831
+ */
832
+ screenOf?: (route: string, url: string) => string | null;
833
+ /** `prod`, `dev` or `local`; worked out from the address when not given (`environmentOf`). */
834
+ environment?: () => string;
835
+ /** where the unsent text is kept on the phone; `<id>.report.draft` when not given. */
836
+ draftKey?: string;
837
+ /**
838
+ * whether this account may read `GET /feedback` (only the owner may), which the never-twice
839
+ * check needs after a failed save. false when not given.
840
+ */
841
+ readsReports?: () => boolean;
842
+ }
843
+ /** the app's `ReportApp`. */
844
+ export declare const REPORT_APP: InjectionToken<ReportApp>;
782
845
  /** what a names tile holds: a name, or the plus. */
783
846
  export type NamesData = {
784
847
  name: ContextName;
@@ -862,6 +925,15 @@ export interface AddWay {
862
925
  * it is only the plus, and then the app opens the name at once instead of this level).
863
926
  */
864
927
  export declare function addLevel(ways: readonly AddWay[]): ContextTile<AddWay>[];
928
+ /** what a tell tile holds: the kind of report, or null for not known yet. */
929
+ export type TellData = ReportKind | null;
930
+ /**
931
+ * tell ("tell us"): what i want to tell about the held piece, as four pieces: something broke, make
932
+ * it better, an idea, and not known yet (said without choosing; empty is the point). a tap ends the
933
+ * journey: the app opens its own sheet with the kind in `data` and the held piece's key, and sends
934
+ * it with `ReportClient`. no kind is in use: nothing has been told yet.
935
+ */
936
+ export declare function tellLevel(words: ContextWords): ContextTile<TellData>[];
865
937
  /** how close a square's places are: from one big tile (0) to only its icon or small cube (4). */
866
938
  export type SquareLevel = 0 | 1 | 2 | 3 | 4;
867
939
  /** cubes' steps (`levelFor`), by a cell's side in px: a tile says less as the square fills. */
@@ -1362,4 +1434,197 @@ export declare class CubeLoading {
1362
1434
  "isSignal": true;
1363
1435
  };
1364
1436
  }, {}, never, ["*", "[cubeLoadingRetry]"], true, never>;
1437
+ }
1438
+ /** how many incidents are kept, and for how long (mosaic's numbers). */
1439
+ export declare const REPORT_INCIDENTS_KEPT = 5;
1440
+ export declare const REPORT_INCIDENTS_MS: number;
1441
+ /** a request's address with its host, query and fragment taken off: `/kitchen/ingredients/abc`. */
1442
+ export declare function scrubbedPath(url: string): string;
1443
+ /** an error as one line: its name and the start of its message. */
1444
+ export declare function errorLine(error: unknown): string;
1445
+ /**
1446
+ * remembers the last few errors and failed requests of the last ten minutes, so a bug report can
1447
+ * carry them. nothing is sent anywhere unless a report is saved with them. a failed request keeps
1448
+ * its method, path and status, never its query or body; an error its name and the first 300
1449
+ * characters of its message.
1450
+ */
1451
+ export declare class ReportDiagnostics {
1452
+ private incidents;
1453
+ constructor();
1454
+ recordError(error: unknown): void;
1455
+ recordFailedRequest(method: string, url: string, status: number): void;
1456
+ /** the incidents of the last ten minutes, oldest first. */
1457
+ recent(now?: number): string[];
1458
+ private add;
1459
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReportDiagnostics, never>;
1460
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1461
+ }
1462
+ /** angular's error handler, which also remembers the error for a bug report. */
1463
+ export declare class ReportErrorHandler extends ErrorHandler {
1464
+ private readonly diagnostics;
1465
+ handleError(error: unknown): void;
1466
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReportErrorHandler, never>;
1467
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1468
+ }
1469
+ /** remembers failed requests, except the report calls themselves (`/feedback`). */
1470
+ export declare const reportInterceptor: HttpInterceptorFn;
1471
+ /**
1472
+ * the error handler, for the app's providers. the interceptor goes in its own
1473
+ * `provideHttpClient(withInterceptors([..., reportInterceptor]))`.
1474
+ */
1475
+ export declare function provideReportDiagnostics(): Provider[];
1476
+ /**
1477
+ * where a report was made, gathered without asking. only what the app and the address say: the
1478
+ * route pattern, the page's path, the app's own words for the screen and the held piece's key;
1479
+ * never a heading, a tile's name or anything a person wrote, never a query or a fragment (a
1480
+ * personal link's token lives in one).
1481
+ */
1482
+ export interface ReportContext {
1483
+ /** the app id sent as `app`. */
1484
+ app: string;
1485
+ /** the app's own words for the screen (`ReportApp.screenOf`), else the route pattern. */
1486
+ screen: string;
1487
+ /** the route pattern: `kitchen/ingredient-catalog/:id`. */
1488
+ route: string;
1489
+ /** the page's address (its base href too) without its query and fragment. */
1490
+ pageUrl: string;
1491
+ /** `prod`, `dev` or `local`. */
1492
+ environment: string;
1493
+ /** the key of the piece held, when the report came from a context cube; else null. */
1494
+ held: string | null;
1495
+ }
1496
+ /** the route pattern of the active route: `kitchen/ingredient-catalog/:id`. */
1497
+ export declare function routePattern(root: ActivatedRouteSnapshot | null): string;
1498
+ /** `local` on localhost, `dev` on a `dev--` or `dev.` host, else `prod`. */
1499
+ export declare function environmentOf(host: string): string;
1500
+ /** the context, from what the app says and where the phone is. pure. */
1501
+ export declare function contextOf(app: ReportApp, at: {
1502
+ url: string;
1503
+ route: string;
1504
+ origin: string;
1505
+ host: string;
1506
+ pathname?: string;
1507
+ }, held?: string | null): ReportContext;
1508
+ /**
1509
+ * where a report is being made now, from `REPORT_APP` and the router; call it in an injection
1510
+ * context (a constructor, a field, `runInInjectionContext`), or use `ReportClient.context`.
1511
+ */
1512
+ export declare function reportContext(opts?: {
1513
+ held?: string | null;
1514
+ }): ReportContext;
1515
+ /**
1516
+ * the technical lines a report carries, in the context note: the piece held, the build, the
1517
+ * screen's size and browser, and (for a bug) the last incidents.
1518
+ */
1519
+ export declare function technicalLines(input: {
1520
+ held?: string | null;
1521
+ build?: ReportBuild | null;
1522
+ size?: {
1523
+ width: number;
1524
+ height: number;
1525
+ } | null;
1526
+ agent?: string | null;
1527
+ incidents?: readonly string[];
1528
+ }): string[];
1529
+ /** how long after a failed save the same text found among the reports counts as that save. */
1530
+ export declare const JUST_SAVED_MINUTES = 15;
1531
+ /** what the never-twice check reads of a saved report. */
1532
+ export interface SavedReportLike {
1533
+ detail?: string | null;
1534
+ /** when it was saved, as the backend sends it. */
1535
+ reported?: string | null;
1536
+ }
1537
+ /**
1538
+ * the report a failed save may have made after all: a save can reach the backend while its answer
1539
+ * is lost (a redeploy, a dropped connection), and saving again would file it twice. the first
1540
+ * report with the same text saved in the last 15 minutes, or undefined. from mosaic's
1541
+ * `findJustSaved`.
1542
+ */
1543
+ export declare function findJustSaved<R extends SavedReportLike>(reports: readonly R[], detail: string, now?: Date): R | undefined;
1544
+ /**
1545
+ * the unsent text, kept on this phone under the app's key, so a sheet closed by mistake opens
1546
+ * with it again. a convenience: without storage it is simply not kept.
1547
+ */
1548
+ export declare class ReportDraft {
1549
+ readonly key: string;
1550
+ constructor(key: string);
1551
+ read(): string;
1552
+ /** keeps the text; an empty one (spaces too) forgets it. */
1553
+ write(text: string): void;
1554
+ clear(): void;
1555
+ }
1556
+ /** what the person said. */
1557
+ export interface ReportInput {
1558
+ /** bug, improvement, idea, or null: not known yet. */
1559
+ kind: ReportKind | null;
1560
+ /** their words, as they wrote or said them. */
1561
+ text: string;
1562
+ /** the held piece's key, when it came from a context cube's "tell us". */
1563
+ held?: string | null;
1564
+ /** whether the last errors and failed requests go with it; a bug's do by default. */
1565
+ withIncidents?: boolean;
1566
+ }
1567
+ /** the body of `POST /feedback` (lang-app-be `NewFeedbackRequest`). */
1568
+ export interface ReportRequest {
1569
+ /** left out when not known yet. */
1570
+ type?: ReportKind;
1571
+ detail: string;
1572
+ app: string;
1573
+ screen: string;
1574
+ pageUrl: string;
1575
+ route: string;
1576
+ environment: string;
1577
+ /** the technical lines, one a line. */
1578
+ context: string;
1579
+ }
1580
+ /** what the backend answers for a saved report; the app reads what it needs (the id for undo). */
1581
+ export interface SavedReport extends SavedReportLike {
1582
+ id: string;
1583
+ number?: number | null;
1584
+ [field: string]: unknown;
1585
+ }
1586
+ /**
1587
+ * why the backend refused a report, in its own plain words, to show as it came (a 400: more than 20
1588
+ * reports in a day, a text too long, an app the account does not use); null for any other failure,
1589
+ * which is worth trying again.
1590
+ */
1591
+ export declare function reportRefusal(error: unknown): string | null;
1592
+ /** the body a report is sent with, from what was said and where. pure. */
1593
+ export declare function reportRequest(input: ReportInput, ctx: ReportContext, technical: readonly string[]): ReportRequest;
1594
+ /**
1595
+ * sends a report to the backend's `POST /feedback`, the one door every account may use, and takes
1596
+ * it back (`undo`, the reporter's own within 15 minutes). it never reads the reports, except for
1597
+ * the never-twice check when `REPORT_APP.readsReports` says this account may (the owner).
1598
+ */
1599
+ export declare class ReportClient {
1600
+ private readonly http;
1601
+ private readonly app;
1602
+ private readonly diagnostics;
1603
+ private readonly injector;
1604
+ private readonly url;
1605
+ /** the draft, under the app's key. */
1606
+ readonly draft: ReportDraft;
1607
+ /** where a report is being made now. */
1608
+ context(held?: string | null): ReportContext;
1609
+ /** the last errors and failed requests, for "what goes with it". */
1610
+ incidents(): string[];
1611
+ /** the body that `capture` would send now, for a sheet that shows what goes with it. */
1612
+ request(input: ReportInput, ctx?: ReportContext): ReportRequest;
1613
+ /**
1614
+ * sends it; the draft is forgotten once the backend has it. the same words sent again within 15
1615
+ * minutes come back as the same report (the backend's never twice). a refusal's words are
1616
+ * `reportRefusal(error)`.
1617
+ */
1618
+ capture(input: ReportInput, ctx?: ReportContext): Observable<SavedReport>;
1619
+ /**
1620
+ * after a failed save: the report it may have made after all, or null, so the sheet can say it was
1621
+ * told instead of sending again. only an account that may read the reports can know
1622
+ * (`readsReports`); for anyone else it is null. sending again is safe for everyone: the backend
1623
+ * answers the same words from the same reporter within 15 minutes with the report it already has.
1624
+ */
1625
+ justSaved(text: string): Observable<SavedReport | null>;
1626
+ /** takes a report back: the undo right after saving. */
1627
+ undo(id: string): Observable<void>;
1628
+ static ɵfac: i0.ɵɵFactoryDeclaration<ReportClient, never>;
1629
+ static ɵprov: i0.ɵɵInjectableDeclaration<any>;
1365
1630
  }