@godxjp/ui 31.10.3 → 31.11.1

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.
@@ -4006,6 +4006,103 @@ export declare const COMPONENT_PROP_REGISTRY: {
4006
4006
  readonly reason: "Escape hatch replacing a box's content; the box, connectors and keyboard stay the library's.";
4007
4007
  }];
4008
4008
  };
4009
+ readonly ImagePreviewItemProp: {
4010
+ readonly group: "data-display";
4011
+ readonly file: "components/data-display.prop.ts";
4012
+ readonly vocabulary: readonly [{
4013
+ readonly field: "src";
4014
+ readonly local: true;
4015
+ readonly reason: "The native img src attribute.";
4016
+ }, {
4017
+ readonly field: "alt";
4018
+ readonly local: true;
4019
+ readonly reason: "The native img alt attribute.";
4020
+ }];
4021
+ };
4022
+ readonly ImagePreviewConfigProp: {
4023
+ readonly group: "data-display";
4024
+ readonly file: "components/data-display.prop.ts";
4025
+ readonly vocabulary: readonly ["OnOpenChangeProp", {
4026
+ readonly field: "visible";
4027
+ readonly local: true;
4028
+ readonly reason: "antd Image `preview.visible`, kept verbatim — the component is a whole antd port.";
4029
+ }, {
4030
+ readonly field: "onVisibleChange";
4031
+ readonly local: true;
4032
+ readonly reason: "antd `preview.onVisibleChange(visible, prevVisible)`, kept verbatim.";
4033
+ }, {
4034
+ readonly field: "src";
4035
+ readonly local: true;
4036
+ readonly reason: "A different file for the preview than the page's.";
4037
+ }, {
4038
+ readonly field: "mask";
4039
+ readonly local: true;
4040
+ readonly reason: "antd's hover veil content over the thumbnail.";
4041
+ }, {
4042
+ readonly field: "scaleStep";
4043
+ readonly local: true;
4044
+ readonly reason: "antd zoom step (fraction added per zoom-in).";
4045
+ }, {
4046
+ readonly field: "minScale";
4047
+ readonly local: true;
4048
+ readonly reason: "antd lower zoom bound.";
4049
+ }, {
4050
+ readonly field: "maxScale";
4051
+ readonly local: true;
4052
+ readonly reason: "antd upper zoom bound.";
4053
+ }];
4054
+ };
4055
+ readonly ImagePreviewGroupConfigProp: {
4056
+ readonly group: "data-display";
4057
+ readonly file: "components/data-display.prop.ts";
4058
+ readonly vocabulary: readonly ["OnValueChangeProp", {
4059
+ readonly field: "current";
4060
+ readonly local: true;
4061
+ readonly reason: "antd PreviewGroup `preview.current` (index on show), kept verbatim.";
4062
+ }, {
4063
+ readonly field: "onChange";
4064
+ readonly local: true;
4065
+ readonly reason: "antd PreviewGroup `preview.onChange(current, prevCurrent)`, kept verbatim.";
4066
+ }];
4067
+ };
4068
+ readonly ImageProp: {
4069
+ readonly group: "data-display";
4070
+ readonly file: "components/data-display.prop.ts";
4071
+ readonly vocabulary: readonly ["ClassNameProp", "PlaceholderProp", {
4072
+ readonly field: "src";
4073
+ readonly local: true;
4074
+ readonly reason: "The native img src attribute.";
4075
+ }, {
4076
+ readonly field: "alt";
4077
+ readonly local: true;
4078
+ readonly reason: "The native img alt contract, made required so it cannot be forgotten.";
4079
+ }, {
4080
+ readonly field: "fallback";
4081
+ readonly local: true;
4082
+ readonly reason: "antd Image `fallback` — the src shown on error.";
4083
+ }, {
4084
+ readonly field: "preview";
4085
+ readonly local: true;
4086
+ readonly reason: "antd Image `preview: boolean | PreviewType` — opt-out or configure the preview.";
4087
+ }];
4088
+ };
4089
+ readonly ImagePreviewGroupProp: {
4090
+ readonly group: "data-display";
4091
+ readonly file: "components/data-display.prop.ts";
4092
+ readonly vocabulary: readonly ["ChildrenProp", {
4093
+ readonly field: "items";
4094
+ readonly local: true;
4095
+ readonly reason: "antd PreviewGroup `items` — an explicit list.";
4096
+ }, {
4097
+ readonly field: "fallback";
4098
+ readonly local: true;
4099
+ readonly reason: "antd PreviewGroup `fallback`.";
4100
+ }, {
4101
+ readonly field: "preview";
4102
+ readonly local: true;
4103
+ readonly reason: "antd PreviewGroup `preview: boolean | { visible, current, onChange, … }`.";
4104
+ }];
4105
+ };
4009
4106
  };
4010
4107
  export type ComponentPropName = keyof typeof COMPONENT_PROP_REGISTRY;
4011
4108
  /** Forbidden duplicate concepts — use the canonical name on the left. */
@@ -4872,6 +4872,87 @@ const COMPONENT_PROP_REGISTRY = {
4872
4872
  reason: "Escape hatch replacing a box's content; the box, connectors and keyboard stay the library's."
4873
4873
  }
4874
4874
  ]
4875
+ },
4876
+ ImagePreviewItemProp: {
4877
+ group: "data-display",
4878
+ file: "components/data-display.prop.ts",
4879
+ vocabulary: [
4880
+ { field: "src", local: true, reason: "The native img src attribute." },
4881
+ { field: "alt", local: true, reason: "The native img alt attribute." }
4882
+ ]
4883
+ },
4884
+ ImagePreviewConfigProp: {
4885
+ group: "data-display",
4886
+ file: "components/data-display.prop.ts",
4887
+ vocabulary: [
4888
+ "OnOpenChangeProp",
4889
+ {
4890
+ field: "visible",
4891
+ local: true,
4892
+ reason: "antd Image `preview.visible`, kept verbatim \u2014 the component is a whole antd port."
4893
+ },
4894
+ {
4895
+ field: "onVisibleChange",
4896
+ local: true,
4897
+ reason: "antd `preview.onVisibleChange(visible, prevVisible)`, kept verbatim."
4898
+ },
4899
+ { field: "src", local: true, reason: "A different file for the preview than the page's." },
4900
+ { field: "mask", local: true, reason: "antd's hover veil content over the thumbnail." },
4901
+ { field: "scaleStep", local: true, reason: "antd zoom step (fraction added per zoom-in)." },
4902
+ { field: "minScale", local: true, reason: "antd lower zoom bound." },
4903
+ { field: "maxScale", local: true, reason: "antd upper zoom bound." }
4904
+ ]
4905
+ },
4906
+ ImagePreviewGroupConfigProp: {
4907
+ group: "data-display",
4908
+ file: "components/data-display.prop.ts",
4909
+ vocabulary: [
4910
+ "OnValueChangeProp",
4911
+ {
4912
+ field: "current",
4913
+ local: true,
4914
+ reason: "antd PreviewGroup `preview.current` (index on show), kept verbatim."
4915
+ },
4916
+ {
4917
+ field: "onChange",
4918
+ local: true,
4919
+ reason: "antd PreviewGroup `preview.onChange(current, prevCurrent)`, kept verbatim."
4920
+ }
4921
+ ]
4922
+ },
4923
+ ImageProp: {
4924
+ group: "data-display",
4925
+ file: "components/data-display.prop.ts",
4926
+ vocabulary: [
4927
+ "ClassNameProp",
4928
+ "PlaceholderProp",
4929
+ { field: "src", local: true, reason: "The native img src attribute." },
4930
+ {
4931
+ field: "alt",
4932
+ local: true,
4933
+ reason: "The native img alt contract, made required so it cannot be forgotten."
4934
+ },
4935
+ { field: "fallback", local: true, reason: "antd Image `fallback` \u2014 the src shown on error." },
4936
+ {
4937
+ field: "preview",
4938
+ local: true,
4939
+ reason: "antd Image `preview: boolean | PreviewType` \u2014 opt-out or configure the preview."
4940
+ }
4941
+ ]
4942
+ },
4943
+ ImagePreviewGroupProp: {
4944
+ group: "data-display",
4945
+ file: "components/data-display.prop.ts",
4946
+ vocabulary: [
4947
+ "ChildrenProp",
4948
+ { field: "items", local: true, reason: "antd PreviewGroup `items` \u2014 an explicit list." },
4949
+ { field: "fallback", local: true, reason: "antd PreviewGroup `fallback`." },
4950
+ {
4951
+ field: "preview",
4952
+ local: true,
4953
+ reason: "antd PreviewGroup `preview: boolean | { visible, current, onChange, \u2026 }`."
4954
+ }
4955
+ ]
4875
4956
  }
4876
4957
  };
4877
4958
  const PROP_ALIASES_FORBIDDEN = {
@@ -2655,3 +2655,206 @@
2655
2655
  }
2656
2656
  }
2657
2657
  }
2658
+
2659
+ @layer components {
2660
+
2661
+ .ui-image {
2662
+ position: relative;
2663
+ display: inline-block;
2664
+ max-inline-size: 100%;
2665
+ padding: 0;
2666
+ border: 0;
2667
+ border-radius: var(--image-radius, var(--radius));
2668
+ background: none;
2669
+ color: inherit;
2670
+ font: inherit;
2671
+ line-height: 0;
2672
+ vertical-align: middle;
2673
+ }
2674
+
2675
+ .ui-image[data-preview] {
2676
+ cursor: zoom-in;
2677
+ }
2678
+
2679
+ .ui-image-img {
2680
+ display: block;
2681
+ max-inline-size: 100%;
2682
+ block-size: auto;
2683
+ border-radius: inherit;
2684
+ }
2685
+
2686
+ .ui-image-placeholder {
2687
+ position: absolute;
2688
+ inset: 0;
2689
+ display: grid;
2690
+ place-items: center;
2691
+ border-radius: inherit;
2692
+ background: hsl(var(--muted));
2693
+ color: hsl(var(--muted-foreground));
2694
+ }
2695
+
2696
+ .ui-image-mask {
2697
+ position: absolute;
2698
+ inset: 0;
2699
+ display: flex;
2700
+ align-items: center;
2701
+ justify-content: center;
2702
+ gap: var(--image-mask-gap);
2703
+ border-radius: inherit;
2704
+ background: var(--image-mask-background, var(--overlay-background));
2705
+ color: var(--image-mask-foreground, hsl(var(--background)));
2706
+ font-size: var(--font-size-sm, var(--font-size-base));
2707
+ line-height: var(--line-height-normal, 1.5);
2708
+ opacity: 0;
2709
+ transition: opacity var(--duration-fast) var(--ease-standard);
2710
+ pointer-events: none;
2711
+ }
2712
+
2713
+ .ui-image-mask-icon {
2714
+ inline-size: var(--image-preview-icon-size);
2715
+ block-size: var(--image-preview-icon-size);
2716
+ }
2717
+
2718
+ .ui-image:hover .ui-image-mask,
2719
+ .ui-image:focus-visible .ui-image-mask {
2720
+ opacity: 1;
2721
+ }
2722
+
2723
+ .ui-image-preview-overlay {
2724
+ position: fixed;
2725
+ inset: 0;
2726
+ z-index: var(--overlay-z-index);
2727
+ background-color: var(--image-preview-background, var(--overlay-background));
2728
+ }
2729
+
2730
+ .ui-image-preview {
2731
+ position: fixed;
2732
+ inset: 0;
2733
+ z-index: var(--overlay-z-index);
2734
+ color: var(--image-preview-foreground, hsl(var(--background)));
2735
+ outline: none;
2736
+ }
2737
+
2738
+ .ui-image-preview-stage {
2739
+ position: absolute;
2740
+ inset: 0;
2741
+ display: flex;
2742
+ align-items: center;
2743
+ justify-content: center;
2744
+ overflow: hidden;
2745
+ padding: calc(var(--image-preview-button-size) + var(--image-preview-inset) * 2);
2746
+ }
2747
+
2748
+ .ui-image-preview-img {
2749
+ max-inline-size: 100%;
2750
+ max-block-size: 100%;
2751
+ object-fit: contain;
2752
+ cursor: grab;
2753
+ user-select: none;
2754
+ touch-action: none;
2755
+ transition: transform var(--duration-base) var(--ease-standard);
2756
+ }
2757
+
2758
+ .ui-image-preview-stage[data-dragging] .ui-image-preview-img {
2759
+ cursor: grabbing;
2760
+ transition: none;
2761
+ }
2762
+
2763
+ @media (prefers-reduced-motion: reduce) {
2764
+ .ui-image-preview-img,
2765
+ .ui-image-mask {
2766
+ transition: none;
2767
+ }
2768
+ }
2769
+
2770
+ .ui-image-preview-button {
2771
+ display: inline-grid;
2772
+ place-items: center;
2773
+ inline-size: var(--image-preview-button-size);
2774
+ block-size: var(--image-preview-button-size);
2775
+ padding: 0;
2776
+ border: 0;
2777
+ border-radius: var(--radius-pill);
2778
+ background: color-mix(
2779
+ in srgb,
2780
+ currentColor var(--image-preview-button-background-alpha),
2781
+ transparent
2782
+ );
2783
+ color: inherit;
2784
+ cursor: pointer;
2785
+ transition: background-color var(--duration-fast) var(--ease-standard);
2786
+ }
2787
+
2788
+ .ui-image-preview-button:hover:not(:disabled) {
2789
+ background: color-mix(
2790
+ in srgb,
2791
+ currentColor var(--image-preview-button-hover-background-alpha),
2792
+ transparent
2793
+ );
2794
+ }
2795
+
2796
+ .ui-image-preview-button:disabled {
2797
+ opacity: var(--disabled-opacity, 0.5);
2798
+ cursor: not-allowed;
2799
+ }
2800
+
2801
+ .ui-image-preview-button svg {
2802
+ inline-size: var(--image-preview-icon-size);
2803
+ block-size: var(--image-preview-icon-size);
2804
+ }
2805
+
2806
+ .ui-image-preview-close {
2807
+ position: absolute;
2808
+ inset-block-start: var(--image-preview-inset);
2809
+ inset-inline-end: var(--image-preview-inset);
2810
+ }
2811
+
2812
+ .ui-image-preview-switch {
2813
+ position: absolute;
2814
+ inset-block-start: 50%;
2815
+ transform: translateY(-50%);
2816
+ }
2817
+
2818
+ .ui-image-preview-switch[data-direction="previous"] {
2819
+ inset-inline-start: var(--image-preview-inset);
2820
+ }
2821
+
2822
+ .ui-image-preview-switch[data-direction="next"] {
2823
+ inset-inline-end: var(--image-preview-inset);
2824
+ }
2825
+
2826
+ [dir="rtl"] .ui-image-preview-switch svg {
2827
+ transform: scaleX(-1);
2828
+ }
2829
+
2830
+ .ui-image-preview-footer {
2831
+ position: absolute;
2832
+ inset-inline: 0;
2833
+ inset-block-end: var(--image-preview-inset);
2834
+ display: flex;
2835
+ flex-direction: column;
2836
+ align-items: center;
2837
+ gap: var(--image-preview-gap);
2838
+ pointer-events: none;
2839
+ }
2840
+
2841
+ .ui-image-preview-footer > * {
2842
+ pointer-events: auto;
2843
+ }
2844
+
2845
+ .ui-image-preview-counter {
2846
+ font-size: var(--font-size-sm, var(--font-size-base));
2847
+ font-variant-numeric: tabular-nums;
2848
+ }
2849
+
2850
+ .ui-image-preview-toolbar {
2851
+ display: flex;
2852
+ flex-wrap: wrap;
2853
+ justify-content: center;
2854
+ gap: var(--image-preview-gap);
2855
+ }
2856
+
2857
+ .ui-image-preview-icon {
2858
+ display: contents;
2859
+ }
2860
+ }
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "31.10.3",
2
+ "version": "31.11.1",
3
3
  "generatedBy": "scripts/gen-style-layers.mjs — do not edit by hand; run `pnpm gen:style-layers`",
4
4
  "base": "base.css",
5
5
  "fonts": "fonts.css",
@@ -575,6 +575,11 @@
575
575
  "icon-layout.css"
576
576
  ]
577
577
  },
578
+ "Image": {
579
+ "layers": [
580
+ "data-display-layout.css"
581
+ ]
582
+ },
578
583
  "InfiniteQueryState": {
579
584
  "layers": [
580
585
  "layout.css",
@@ -39,6 +39,7 @@
39
39
  @import "./components/tree.css";
40
40
  @import "./components/thought-chain.css";
41
41
  @import "./components/org-chart.css";
42
+ @import "./components/image.css";
42
43
  @import "./components/welcome.css";
43
44
  @import "./components/actions.css";
44
45
  @import "./components/attachments.css";
@@ -0,0 +1,19 @@
1
+
2
+
3
+ :root {
4
+
5
+ --image-mask-background: initial;
6
+ --image-mask-foreground: initial;
7
+ --image-mask-gap: var(--space-1);
8
+ --image-radius: initial;
9
+
10
+ --image-preview-background: initial;
11
+ --image-preview-foreground: initial;
12
+ --image-preview-inset: var(--space-4);
13
+ --image-preview-gap: var(--space-2);
14
+
15
+ --image-preview-button-size: 2.5rem;
16
+ --image-preview-button-background-alpha: 10%;
17
+ --image-preview-button-hover-background-alpha: 20%;
18
+ --image-preview-icon-size: var(--icon-size-lg);
19
+ }
@@ -3,8 +3,8 @@
3
3
  > Generated by `scripts/frame-coverage.mjs` (issue #163). Do not edit by hand — run `pnpm check:frame-coverage`.
4
4
  > Standard: [FRAME-COVERAGE-STANDARD.md](./FRAME-COVERAGE-STANDARD.md). A missing dimension is **UNTESTED**, never a pass.
5
5
 
6
- - Public components: **177**
7
- - With ≥1 frame: **153** · zero-frame: **24**
6
+ - Public components: **178**
7
+ - With ≥1 frame: **154** · zero-frame: **24**
8
8
  - Fully covered (every contract axis declared): **0**
9
9
 
10
10
  ## UNTESTED — no `/frame/**` route
@@ -212,6 +212,7 @@ Legend: ✓ covered · N/A reasoned skip · · UNTESTED (blank cell = UNTESTED).
212
212
  | Actions | ✓ | · | N/A | N/A | · | · | · | · | · | · |
213
213
  | ThoughtChain | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
214
214
  | OrgChart | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
215
+ | Image | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
215
216
  | Attachments | ✓ | N/A | · | N/A | · | · | · | · | · | · |
216
217
  | Marquee | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
217
218
  | Masonry | ✓ | N/A | N/A | N/A | · | · | · | · | · | · |
@@ -0,0 +1,130 @@
1
+ import * as React from "react";
2
+
3
+ import {
4
+ Card,
5
+ CardContent,
6
+ CardDescription,
7
+ CardHeader,
8
+ CardTitle,
9
+ Image,
10
+ ImagePreviewGroup,
11
+ Prose,
12
+ } from "@godxjp/ui/data-display";
13
+ import { Button, Text } from "@godxjp/ui/general";
14
+ import { Flex, PageContainer } from "@godxjp/ui/layout";
15
+
16
+ import coverTerrain from "../assets/cover-terrain.svg";
17
+ import portraitIris from "../assets/portrait-iris.svg";
18
+ import shotLandscape from "../assets/shot-landscape.svg";
19
+ import shotPortrait from "../assets/shot-portrait.svg";
20
+
21
+ /* Committed SVG files under docs/assets — a docs page never fetches a third-party image. */
22
+
23
+ /**
24
+ * Image + ImagePreviewGroup — antd `Image` / `Image.PreviewGroup` (gh#1077).
25
+ *
26
+ * Click (or Enter on) a picture: it opens full-viewport with zoom in/out (buttons, wheel,
27
+ * double-click), rotate, flip and reset; drag pans a zoomed picture. Inside a group, ←/→ and the
28
+ * side buttons page every picture of the group with a "2 / 3" counter. Esc closes and focus goes
29
+ * back to the picture.
30
+ *
31
+ * Deviations from antd, on purpose: the thumbnail is a real button (keyboard-openable); the
32
+ * preview traps focus and restores it; ←/→ follow the reading direction (RTL: ← is next); with
33
+ * `items`, a clicked child opens at its own src (antd opens 0); `alt` is required; the
34
+ * `toolbarRender` / `imageRender` / `countRender` render props, `movable` and `getContainer` are
35
+ * not ported.
36
+ */
37
+ export default function Demo() {
38
+ const [current, setCurrent] = React.useState(0);
39
+ const [visible, setVisible] = React.useState(false);
40
+
41
+ return (
42
+ <PageContainer title="Image" subtitle="Click to preview large · ←/→ page through a group">
43
+ <Flex direction="col" gap="lg">
44
+ <Card>
45
+ <CardHeader>
46
+ <CardTitle level={2}>Every screenshot of a Markdown body in one preview</CardTitle>
47
+ <CardDescription>
48
+ Every Image under an ImagePreviewGroup, however deep, joins one gallery in document
49
+ order. Mapping a Markdown renderer&apos;s img to Image is enough to page through the
50
+ article&apos;s screenshots.
51
+ </CardDescription>
52
+ </CardHeader>
53
+ <CardContent>
54
+ <ImagePreviewGroup>
55
+ <Prose>
56
+ <h3>Test result</h3>
57
+ <p>Before the fix:</p>
58
+ <p>
59
+ <Image src={shotPortrait} width={180} alt="List screen before the fix" />
60
+ </p>
61
+ <p>After the fix:</p>
62
+ <p>
63
+ <Image src={shotLandscape} width={320} alt="Dashboard after the fix" />
64
+ </p>
65
+ <p>
66
+ The logo leaves the group with preview=&#123;false&#125;:{" "}
67
+ <Image src={portraitIris} width={48} alt="App icon" preview={false} />
68
+ </p>
69
+ </Prose>
70
+ </ImagePreviewGroup>
71
+ </CardContent>
72
+ </Card>
73
+
74
+ <Card>
75
+ <CardHeader>
76
+ <CardTitle level={2}>items and controlled preview.current / visible</CardTitle>
77
+ <CardDescription>
78
+ `items` lists the gallery explicitly; `preview` controls current / onChange and
79
+ visible / onVisibleChange with antd&apos;s (next, prev) argument order.
80
+ </CardDescription>
81
+ </CardHeader>
82
+ <CardContent>
83
+ <Flex direction="col" gap="sm">
84
+ <Text tone="muted">
85
+ Showing {current + 1} / 3 · {visible ? "open" : "closed"}
86
+ </Text>
87
+ <Flex gap="sm" wrap>
88
+ <Button onClick={() => setVisible(true)}>Open the gallery</Button>
89
+ </Flex>
90
+ <ImagePreviewGroup
91
+ items={[
92
+ { src: shotPortrait, alt: "List screen" },
93
+ { src: shotLandscape, alt: "Dashboard" },
94
+ { src: coverTerrain, alt: "Cover image" },
95
+ ]}
96
+ preview={{
97
+ current,
98
+ visible,
99
+ onChange: (next) => setCurrent(next),
100
+ onVisibleChange: (next) => setVisible(next),
101
+ }}
102
+ />
103
+ </Flex>
104
+ </CardContent>
105
+ </Card>
106
+
107
+ <Card>
108
+ <CardHeader>
109
+ <CardTitle level={2}>Standalone · fallback · placeholder</CardTitle>
110
+ <CardDescription>
111
+ An Image outside a group previews only itself. A picture that fails to load shows its
112
+ fallback and does not open a preview (as in antd).
113
+ </CardDescription>
114
+ </CardHeader>
115
+ <CardContent>
116
+ <Flex gap="md" wrap align="start">
117
+ <Image src={coverTerrain} width={240} alt="Cover image" placeholder />
118
+ <Image
119
+ src="/missing-screenshot.png"
120
+ fallback={portraitIris}
121
+ width={96}
122
+ alt="A picture that failed to load"
123
+ />
124
+ </Flex>
125
+ </CardContent>
126
+ </Card>
127
+ </Flex>
128
+ </PageContainer>
129
+ );
130
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@godxjp/ui",
3
- "version": "31.10.3",
4
- "godxUiMcp": "31.10.3",
3
+ "version": "31.11.1",
4
+ "godxUiMcp": "31.11.1",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
7
7
  "type": "git",