ghcr-cleanup-manager-visualizer 1.1.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.
@@ -0,0 +1,118 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
6
+ <title>GHCR Cleanup Manager Visualizer</title>
7
+ <link rel="stylesheet" href="/styles.css" />
8
+ </head>
9
+ <body>
10
+ <main class="layout">
11
+ <section class="controls">
12
+ <h1>GHCR Cleanup Manager Visualizer</h1>
13
+ <form id="search-form" autocomplete="off">
14
+ <label>
15
+ Owner
16
+ <select id="owner" name="owner" required>
17
+ <option value="">Select owner</option>
18
+ </select>
19
+ </label>
20
+ <label>
21
+ Package
22
+ <select id="package" name="package" required disabled>
23
+ <option value="">Select package</option>
24
+ </select>
25
+ </label>
26
+ <label>
27
+ Scan ID
28
+ <select id="scan-id" name="scan_id" disabled>
29
+ <option value="">Latest completed scan</option>
30
+ </select>
31
+ </label>
32
+ <label>
33
+ Compare Scan ID
34
+ <select id="compare-scan-id" name="compare_scan_id" disabled>
35
+ <option value="">None</option>
36
+ </select>
37
+ </label>
38
+ <label>
39
+ Lookup
40
+ <select id="lookup-mode" name="lookup_mode">
41
+ <option value="tag">Tag</option>
42
+ <option value="digest">Digest</option>
43
+ </select>
44
+ </label>
45
+ <label>
46
+ Value
47
+ <input id="lookup-value" name="lookup_value" list="lookup-suggestions" autocomplete="off" required />
48
+ </label>
49
+ <label>
50
+ Depth
51
+ <input id="depth" name="depth" type="number" min="0" max="100" value="12" required />
52
+ </label>
53
+ <button type="submit">Load graph</button>
54
+ </form>
55
+ <div class="legend">
56
+ <h2>Edges</h2>
57
+ <p><span class="legend-line solid"></span> image-child</p>
58
+ <p><span class="legend-line dashed"></span> referrer</p>
59
+ <p><span class="legend-line dotted"></span> digest-tag-referrer</p>
60
+ </div>
61
+ <div id="status" class="status"></div>
62
+ </section>
63
+ <section class="graph-panel">
64
+ <div class="graph-toolbar" aria-label="Graph controls">
65
+ <button id="toggle-grid" type="button" title="Toggle screenshot grid" aria-pressed="false">Grid</button>
66
+ <button id="zoom-in" type="button" title="Zoom in">+</button>
67
+ <button id="zoom-out" type="button" title="Zoom out">-</button>
68
+ <button id="zoom-fit" type="button" title="Fit graph">Fit</button>
69
+ </div>
70
+ <div id="graph-grid" class="graph-grid" hidden aria-hidden="true"></div>
71
+ <div id="graph"></div>
72
+ </section>
73
+ <aside class="details">
74
+ <h2>Manifest</h2>
75
+ <div id="details-empty">Select a manifest.</div>
76
+ <dl id="details" hidden>
77
+ <dt>Actions</dt>
78
+ <dd>
79
+ <div class="detail-actions">
80
+ <button id="expand-node" type="button" disabled>Expand 1 hop</button>
81
+ <button id="center-node" type="button" disabled>Center here</button>
82
+ <button id="show-raw-json" type="button" disabled>View raw JSON</button>
83
+ </div>
84
+ </dd>
85
+ <dt>Digest</dt>
86
+ <dd id="detail-digest"></dd>
87
+ <dt>Version</dt>
88
+ <dd id="detail-version"></dd>
89
+ <dt>Created</dt>
90
+ <dd id="detail-created-at"></dd>
91
+ <dt>Updated</dt>
92
+ <dd id="detail-updated-at"></dd>
93
+ <dt>Kind</dt>
94
+ <dd id="detail-kind"></dd>
95
+ <dt>Media Type</dt>
96
+ <dd id="detail-media-type"></dd>
97
+ <dt>Platform</dt>
98
+ <dd id="detail-platform"></dd>
99
+ <dt>Artifact Type</dt>
100
+ <dd id="detail-artifact-type"></dd>
101
+ <dt>Subject</dt>
102
+ <dd id="detail-subject"></dd>
103
+ <dt>Tags</dt>
104
+ <dd id="detail-tags"></dd>
105
+ </dl>
106
+ </aside>
107
+ </main>
108
+ <datalist id="lookup-suggestions"></datalist>
109
+ <dialog id="raw-json-dialog" class="raw-json-dialog">
110
+ <div class="raw-json-dialog__header">
111
+ <h2>Raw JSON</h2>
112
+ <button id="close-raw-json" type="button">Close</button>
113
+ </div>
114
+ <pre id="raw-json-content"></pre>
115
+ </dialog>
116
+ <script type="module" src="/app.js"></script>
117
+ </body>
118
+ </html>
@@ -0,0 +1,257 @@
1
+ body {
2
+ margin: 0;
3
+ font-family: "IBM Plex Sans", "Segoe UI", sans-serif;
4
+ color: #112218;
5
+ }
6
+
7
+ .layout {
8
+ display: grid;
9
+ grid-template-columns: 20rem 1fr 24rem;
10
+ min-height: 100vh;
11
+ }
12
+
13
+ .controls,
14
+ .details {
15
+ padding: 1rem;
16
+ min-width: 0;
17
+ }
18
+
19
+ .controls {
20
+ border-right: 1px solid #b7c8bf;
21
+ position: relative;
22
+ z-index: 1;
23
+ }
24
+
25
+ .details {
26
+ border-left: 1px solid #b7c8bf;
27
+ overflow: auto;
28
+ }
29
+
30
+ .graph-panel {
31
+ position: relative;
32
+ min-width: 0;
33
+ }
34
+
35
+ .graph-toolbar {
36
+ position: absolute;
37
+ top: 1rem;
38
+ right: 1rem;
39
+ display: flex;
40
+ flex-wrap: wrap;
41
+ justify-content: flex-end;
42
+ gap: 0.5rem;
43
+ z-index: 2;
44
+ }
45
+
46
+ .graph-toolbar button {
47
+ min-width: 3rem;
48
+ background: rgba(29, 79, 58, 0.92);
49
+ border-color: #1d4f3a;
50
+ box-shadow: 0 0.2rem 0.8rem rgba(16, 32, 23, 0.18);
51
+ }
52
+
53
+ #graph {
54
+ width: 100%;
55
+ height: 100vh;
56
+ background: #ffffff;
57
+ }
58
+
59
+ .graph-grid {
60
+ position: absolute;
61
+ inset: 0;
62
+ pointer-events: none;
63
+ z-index: 1;
64
+ background-image:
65
+ linear-gradient(rgba(32, 64, 48, 0.08) 1px, transparent 1px),
66
+ linear-gradient(90deg, rgba(32, 64, 48, 0.08) 1px, transparent 1px),
67
+ linear-gradient(rgba(32, 64, 48, 0.18) 1px, transparent 1px),
68
+ linear-gradient(90deg, rgba(32, 64, 48, 0.18) 1px, transparent 1px);
69
+ background-position:
70
+ 0 0,
71
+ 0 0,
72
+ 0 0,
73
+ 0 0;
74
+ background-size:
75
+ 24px 24px,
76
+ 24px 24px,
77
+ 120px 120px,
78
+ 120px 120px;
79
+ }
80
+
81
+ form {
82
+ display: grid;
83
+ gap: 0.75rem;
84
+ min-width: 0;
85
+ }
86
+
87
+ label {
88
+ display: grid;
89
+ gap: 0.35rem;
90
+ font-size: 0.95rem;
91
+ min-width: 0;
92
+ }
93
+
94
+ input,
95
+ select,
96
+ button {
97
+ font: inherit;
98
+ padding: 0.5rem 0.65rem;
99
+ border-radius: 0.5rem;
100
+ border: 1px solid #94a99d;
101
+ }
102
+
103
+ .controls input,
104
+ .controls select,
105
+ .controls form > button {
106
+ width: 100%;
107
+ max-width: 100%;
108
+ box-sizing: border-box;
109
+ }
110
+
111
+ button {
112
+ cursor: pointer;
113
+ background: #1d4f3a;
114
+ color: #fff;
115
+ border-color: #1d4f3a;
116
+ }
117
+
118
+ .detail-actions {
119
+ display: flex;
120
+ gap: 0.5rem;
121
+ flex-wrap: wrap;
122
+ }
123
+
124
+ .detail-actions button {
125
+ background: #325745;
126
+ border-color: #325745;
127
+ }
128
+
129
+ .raw-json-dialog {
130
+ width: min(70rem, calc(100vw - 4rem));
131
+ height: min(80vh, 52rem);
132
+ border: 1px solid #94a99d;
133
+ border-radius: 0.75rem;
134
+ padding: 0;
135
+ }
136
+
137
+ .raw-json-dialog::backdrop {
138
+ background: rgba(16, 32, 23, 0.45);
139
+ }
140
+
141
+ .raw-json-dialog__header {
142
+ display: flex;
143
+ align-items: center;
144
+ justify-content: space-between;
145
+ gap: 1rem;
146
+ padding: 1rem 1.25rem;
147
+ border-bottom: 1px solid #d1ddd6;
148
+ }
149
+
150
+ .raw-json-dialog__header h2 {
151
+ margin: 0;
152
+ }
153
+
154
+ .raw-json-dialog__header button {
155
+ background: #325745;
156
+ border-color: #325745;
157
+ color: #fff;
158
+ }
159
+
160
+ #raw-json-content {
161
+ margin: 0;
162
+ padding: 1rem 1.25rem 1.5rem;
163
+ height: calc(100% - 4.5rem);
164
+ overflow: auto;
165
+ white-space: pre;
166
+ word-break: normal;
167
+ }
168
+
169
+ .status {
170
+ margin-top: 1rem;
171
+ min-height: 1.5rem;
172
+ font-size: 0.95rem;
173
+ }
174
+
175
+ .legend {
176
+ margin-top: 1.5rem;
177
+ }
178
+
179
+ .legend-line {
180
+ display: inline-block;
181
+ width: 2.5rem;
182
+ border-top: 3px solid #3d5a4a;
183
+ margin-right: 0.5rem;
184
+ vertical-align: middle;
185
+ }
186
+
187
+ .legend-line.dashed {
188
+ border-top-style: dashed;
189
+ }
190
+
191
+ .legend-line.dotted {
192
+ border-top-style: dotted;
193
+ }
194
+
195
+ dl {
196
+ margin: 0;
197
+ }
198
+
199
+ dt {
200
+ font-weight: 700;
201
+ margin-top: 0.9rem;
202
+ }
203
+
204
+ dd {
205
+ margin: 0.2rem 0 0;
206
+ word-break: break-word;
207
+ }
208
+
209
+ .tag-list {
210
+ display: flex;
211
+ flex-wrap: wrap;
212
+ gap: 0.35rem 0.5rem;
213
+ }
214
+
215
+ .tag {
216
+ display: inline-block;
217
+ padding: 0.1rem 0.35rem;
218
+ border-radius: 999px;
219
+ background: #e7eeea;
220
+ }
221
+
222
+ .tag.added {
223
+ color: #1f6f2c;
224
+ background: #d9f0dc;
225
+ }
226
+
227
+ .tag.removed {
228
+ color: #8d1f17;
229
+ background: #f6dbd8;
230
+ }
231
+
232
+ pre {
233
+ margin: 0;
234
+ white-space: pre-wrap;
235
+ word-break: break-word;
236
+ font-size: 0.85rem;
237
+ }
238
+
239
+ @media (max-width: 1100px) {
240
+ .layout {
241
+ grid-template-columns: 1fr;
242
+ }
243
+
244
+ .controls,
245
+ .details {
246
+ border: 0;
247
+ }
248
+
249
+ .graph-toolbar {
250
+ top: 0.75rem;
251
+ right: 0.75rem;
252
+ }
253
+
254
+ #graph {
255
+ height: 60vh;
256
+ }
257
+ }