galadrim-feedback 1.0.63 → 1.0.65
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/README.md +105 -2
- package/bin/cli.js +101 -4
- package/dist/assets/icons/info-circle.svg +3 -0
- package/dist/index.cjs +472 -41
- package/dist/index.mjs +472 -41
- package/dist/types/libs/backend-client/api.d.ts +1199 -59
- package/dist/types/src/components/molecules/feedbacks/menu/FeedbackDetailsModal.d.ts +12 -3
- package/dist/types/src/components/molecules/kanban/LinkToTicketForm.d.ts +1 -1
- package/dist/types/src/contexts/feedback/FeedbackProvider.d.ts +2 -1
- package/dist/types/src/utils/getGitSha.d.ts +16 -0
- package/dist/types/src/utils/getNotionStatusColor.d.ts +1 -0
- package/dist/types/src/utils/isolateFromHostApp.d.ts +36 -0
- package/index.d.ts +5 -0
- package/libs/backend-client/api.ts +2345 -90
- package/libs/backend-client/configuration.ts +96 -117
- package/next.cjs +24 -0
- package/next.d.ts +16 -0
- package/package.json +23 -2
- package/resolveGitSha.cjs +29 -0
- package/src/assets/icons/info-circle.svg +3 -0
- package/src/components/boundary/StyleBoundary.tsx +10 -2
- package/src/components/molecules/feedbacks/item/FeedbackListItem.tsx +38 -1
- package/src/components/molecules/feedbacks/menu/FeedbackDetailsModal.tsx +58 -36
- package/src/components/molecules/feedbacks/menu/MenuButton.tsx +44 -2
- package/src/components/molecules/feedbacks/shared/FeedbackTextArea.tsx +2 -1
- package/src/components/molecules/kanban/LinkToTicketForm.tsx +1 -1
- package/src/contexts/feedback/FeedbackProvider.tsx +5 -1
- package/src/services/feedback.ts +4 -0
- package/src/utils/getGitSha.ts +55 -0
- package/src/utils/getNotionStatusColor.ts +16 -0
- package/src/utils/isolateFromHostApp.ts +106 -0
- package/vite.cjs +29 -0
- package/vite.d.ts +23 -0
- package/webpack.cjs +28 -0
- package/webpack.d.ts +14 -0
package/README.md
CHANGED
|
@@ -47,12 +47,14 @@ Galadrim Feedback requires a plugin to add file path data to your components. We
|
|
|
47
47
|
// vite.config.ts
|
|
48
48
|
import { defineConfig } from "vite";
|
|
49
49
|
import react from "@vitejs/plugin-react-swc";
|
|
50
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
50
51
|
|
|
51
52
|
export default defineConfig({
|
|
52
53
|
plugins: [
|
|
53
54
|
react({
|
|
54
55
|
plugins: [["swc-plugin-react-generate-paths", {}]],
|
|
55
56
|
}),
|
|
57
|
+
includeGitSha(),
|
|
56
58
|
],
|
|
57
59
|
});
|
|
58
60
|
```
|
|
@@ -88,6 +90,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
88
90
|
// vite.config.ts
|
|
89
91
|
import { defineConfig } from "vite";
|
|
90
92
|
import react from "@vitejs/plugin-react";
|
|
93
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
91
94
|
|
|
92
95
|
export default defineConfig({
|
|
93
96
|
plugins: [
|
|
@@ -96,6 +99,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
96
99
|
plugins: [["babel-plugin-react-generate-paths"]],
|
|
97
100
|
},
|
|
98
101
|
}),
|
|
102
|
+
includeGitSha(),
|
|
99
103
|
],
|
|
100
104
|
});
|
|
101
105
|
```
|
|
@@ -108,7 +112,24 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
108
112
|
```
|
|
109
113
|
|
|
110
114
|
|
|
111
|
-
3. Configure
|
|
115
|
+
3. Configure Git SHA tracking:
|
|
116
|
+
Feedbacks automatically record the git commit SHA of the deployed app — no prop is needed on FeedbackRoot. Pick the integration matching the project's build tool:
|
|
117
|
+
- **Vite**: already handled by the `includeGitSha()` plugin added above (it injects a `<meta name="git-sha">` tag at build time).
|
|
118
|
+
- **Next.js** (webpack or Turbopack): wrap the config in `next.config.js`:
|
|
119
|
+
```javascript
|
|
120
|
+
const { withGitSha } = require("galadrim-feedback/next");
|
|
121
|
+
module.exports = withGitSha({ /* existing next config */ });
|
|
122
|
+
```
|
|
123
|
+
(On Vercel this is optional: `NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA` is detected automatically when system environment variables are exposed.)
|
|
124
|
+
- **CRA / custom webpack**: add the webpack plugin (via craco/react-app-rewired for CRA):
|
|
125
|
+
```javascript
|
|
126
|
+
const GitShaPlugin = require("galadrim-feedback/webpack");
|
|
127
|
+
// in the webpack config: plugins: [new GitShaPlugin()]
|
|
128
|
+
```
|
|
129
|
+
- **Any other React setup**: add `npx galadrim-feedback inject-git-sha` to the build/deploy script, right after the build — it patches the built HTML files with the `<meta name="git-sha">` tag.
|
|
130
|
+
- **Docker builds**: the .git folder is usually absent from the build context, so pass the SHA explicitly: `docker build --build-arg GIT_SHA=$(git rev-parse HEAD)` with `ARG GIT_SHA` and `ENV GIT_SHA=$GIT_SHA` in the Dockerfile.
|
|
131
|
+
|
|
132
|
+
4. Configure FeedbackRoot component:
|
|
112
133
|
- Run `pwd` command in the frontend root directory and use this absolute path for rootDir prop
|
|
113
134
|
- Detect navigation package used in the project:
|
|
114
135
|
* If using React Router Dom (react-router-dom):
|
|
@@ -139,7 +160,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
139
160
|
- notionDatabaseId="${NOTION_DATABASE_ID}"
|
|
140
161
|
* Place it inside the router context but outside main content areas, if needed you can wrap it in a layout component that is parent to the rest of the app
|
|
141
162
|
|
|
142
|
-
|
|
163
|
+
5. Verification:
|
|
143
164
|
- Show all modified files
|
|
144
165
|
- Confirm the navigation setup works with the detected router
|
|
145
166
|
- List any manual steps needed (like getting projectId from admin) (see https://feedback.galadrim.ovh/admin)
|
|
@@ -187,12 +208,14 @@ Galadrim Feedback requires a plugin to add file path data to your components.
|
|
|
187
208
|
// vite.config.ts
|
|
188
209
|
import { defineConfig } from "vite";
|
|
189
210
|
import react from "@vitejs/plugin-react-swc";
|
|
211
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
190
212
|
|
|
191
213
|
export default defineConfig({
|
|
192
214
|
plugins: [
|
|
193
215
|
react({
|
|
194
216
|
plugins: [["swc-plugin-react-generate-paths", {}]],
|
|
195
217
|
}),
|
|
218
|
+
includeGitSha(),
|
|
196
219
|
],
|
|
197
220
|
});
|
|
198
221
|
```
|
|
@@ -240,6 +263,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
240
263
|
// vite.config.ts
|
|
241
264
|
import { defineConfig } from "vite";
|
|
242
265
|
import react from "@vitejs/plugin-react";
|
|
266
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
243
267
|
|
|
244
268
|
export default defineConfig({
|
|
245
269
|
plugins: [
|
|
@@ -248,6 +272,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
248
272
|
plugins: [["babel-plugin-react-generate-paths"]],
|
|
249
273
|
},
|
|
250
274
|
}),
|
|
275
|
+
includeGitSha(),
|
|
251
276
|
],
|
|
252
277
|
});
|
|
253
278
|
```
|
|
@@ -260,6 +285,10 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
260
285
|
}
|
|
261
286
|
```
|
|
262
287
|
|
|
288
|
+
### Git SHA / Version Tracking
|
|
289
|
+
|
|
290
|
+
Each feedback records the git commit SHA of the deployed app — see the [Git SHA Setup](#git-sha-setup) section below for the integration matching your build tool.
|
|
291
|
+
|
|
263
292
|
### FeedbackRoot Integration
|
|
264
293
|
|
|
265
294
|
Add the **FeedbackRoot** component to the root of your application. It must be placed inside the **router**.
|
|
@@ -309,6 +338,80 @@ The kanban integration allows you to link feedback items to tickets/cards in you
|
|
|
309
338
|
|
|
310
339
|
</details>
|
|
311
340
|
|
|
341
|
+
## Git SHA Setup
|
|
342
|
+
|
|
343
|
+
Each feedback records the git commit SHA of the app it was created on, so it can be traced back to the exact deployed version. Detection is fully automatic — nothing to pass to `FeedbackRoot`. All integrations resolve the SHA the same way: from `GIT_SHA`, `VERCEL_GIT_COMMIT_SHA`, `CF_PAGES_COMMIT_SHA`, `GITHUB_SHA` or `CI_COMMIT_SHA` env vars, falling back to `git rev-parse HEAD`.
|
|
344
|
+
|
|
345
|
+
### Vite
|
|
346
|
+
|
|
347
|
+
Add the plugin to `vite.config.ts`. It injects a `<meta name="git-sha">` tag at each build (dev server included):
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
351
|
+
|
|
352
|
+
export default defineConfig({
|
|
353
|
+
plugins: [react(), includeGitSha()],
|
|
354
|
+
});
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
### Next.js
|
|
358
|
+
|
|
359
|
+
Works with webpack and Turbopack — wrap your config; the SHA is exposed as `NEXT_PUBLIC_GIT_SHA` and inlined into the client bundle:
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
// next.config.js
|
|
363
|
+
const { withGitSha } = require("galadrim-feedback/next");
|
|
364
|
+
|
|
365
|
+
module.exports = withGitSha({
|
|
366
|
+
// your existing next config
|
|
367
|
+
});
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
On Vercel this is optional: `NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA` is picked up automatically (with "Automatically expose System Environment Variables" enabled).
|
|
371
|
+
|
|
372
|
+
### CRA / custom webpack
|
|
373
|
+
|
|
374
|
+
Add the webpack plugin (via craco or react-app-rewired for CRA):
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
const GitShaPlugin = require("galadrim-feedback/webpack");
|
|
378
|
+
|
|
379
|
+
// in the webpack config
|
|
380
|
+
plugins: [new GitShaPlugin()],
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Any other React setup
|
|
384
|
+
|
|
385
|
+
Run the CLI right after the build; it patches the built HTML files with the `<meta name="git-sha">` tag:
|
|
386
|
+
|
|
387
|
+
```bash
|
|
388
|
+
npx galadrim-feedback inject-git-sha # auto-detects dist/, build/ or out/
|
|
389
|
+
npx galadrim-feedback inject-git-sha dist # or pass the output dir explicitly
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
### Docker builds
|
|
393
|
+
|
|
394
|
+
The `.git` folder is usually absent from the build context, so pass the SHA in explicitly:
|
|
395
|
+
|
|
396
|
+
```bash
|
|
397
|
+
docker build --build-arg GIT_SHA=$(git rev-parse HEAD) .
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
```dockerfile
|
|
401
|
+
ARG GIT_SHA
|
|
402
|
+
ENV GIT_SHA=$GIT_SHA
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
### Manual override
|
|
406
|
+
|
|
407
|
+
Expose a `<meta name="git-sha" content="...">` tag or a `window.__GIT_SHA__` global at deploy time, or pass the `gitSha` prop to `FeedbackRoot`.
|
|
408
|
+
|
|
409
|
+
## Modals and focus traps
|
|
410
|
+
|
|
411
|
+
Galadrim Feedback works on top of modal dialogs (Radix, Headless UI, MUI, react-focus-lock…) without any configuration on your side: you don't need `modal={false}`, `onInteractOutside`, `onOpenAutoFocus` or any other escape hatch on your dialogs.
|
|
412
|
+
|
|
413
|
+
The widget renders in a shadow root and stops its own events from reaching `document`, so the focus traps and "click outside" layers of your app never mistake it for an outside interaction — leaving a feedback on a modal keeps it open and keeps the comment field focusable.
|
|
414
|
+
|
|
312
415
|
## Account Creation
|
|
313
416
|
|
|
314
417
|
Once it's installed, you can sign up and then in the Galadmin, you add yourself as a collaborator on the project
|
package/bin/cli.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { spawn } from "child_process";
|
|
4
|
+
import { existsSync, readdirSync, readFileSync, statSync, writeFileSync } from "fs";
|
|
5
|
+
import { join } from "path";
|
|
6
|
+
import { resolveGitSha } from "../resolveGitSha.cjs";
|
|
4
7
|
|
|
5
8
|
const PROJECT_ID = process.argv
|
|
6
9
|
.find((arg) => arg.startsWith("--project-id="))
|
|
@@ -31,12 +34,14 @@ Galadrim Feedback requires a plugin to add file path data to your components. We
|
|
|
31
34
|
// vite.config.ts
|
|
32
35
|
import { defineConfig } from "vite";
|
|
33
36
|
import react from "@vitejs/plugin-react-swc";
|
|
37
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
34
38
|
|
|
35
39
|
export default defineConfig({
|
|
36
40
|
plugins: [
|
|
37
41
|
react({
|
|
38
42
|
plugins: [["swc-plugin-react-generate-paths", {}]],
|
|
39
43
|
}),
|
|
44
|
+
includeGitSha(),
|
|
40
45
|
],
|
|
41
46
|
});
|
|
42
47
|
\`\`\`
|
|
@@ -72,6 +77,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
72
77
|
// vite.config.ts
|
|
73
78
|
import { defineConfig } from "vite";
|
|
74
79
|
import react from "@vitejs/plugin-react";
|
|
80
|
+
import { includeGitSha } from "galadrim-feedback/vite";
|
|
75
81
|
|
|
76
82
|
export default defineConfig({
|
|
77
83
|
plugins: [
|
|
@@ -80,6 +86,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
80
86
|
plugins: [["babel-plugin-react-generate-paths"]],
|
|
81
87
|
},
|
|
82
88
|
}),
|
|
89
|
+
includeGitSha(),
|
|
83
90
|
],
|
|
84
91
|
});
|
|
85
92
|
\`\`\`
|
|
@@ -92,7 +99,24 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
92
99
|
\`\`\`
|
|
93
100
|
|
|
94
101
|
|
|
95
|
-
3. Configure
|
|
102
|
+
3. Configure Git SHA tracking:
|
|
103
|
+
Feedbacks automatically record the git commit SHA of the deployed app — no prop is needed on FeedbackRoot. Pick the integration matching the project's build tool:
|
|
104
|
+
- **Vite**: already handled by the \`includeGitSha()\` plugin added above (it injects a \`<meta name="git-sha">\` tag at build time).
|
|
105
|
+
- **Next.js** (webpack or Turbopack): wrap the config in \`next.config.js\`:
|
|
106
|
+
\`\`\`javascript
|
|
107
|
+
const { withGitSha } = require("galadrim-feedback/next");
|
|
108
|
+
module.exports = withGitSha({ /* existing next config */ });
|
|
109
|
+
\`\`\`
|
|
110
|
+
(On Vercel this is optional: \`NEXT_PUBLIC_VERCEL_GIT_COMMIT_SHA\` is detected automatically when system environment variables are exposed.)
|
|
111
|
+
- **CRA / custom webpack**: add the webpack plugin (via craco/react-app-rewired for CRA):
|
|
112
|
+
\`\`\`javascript
|
|
113
|
+
const GitShaPlugin = require("galadrim-feedback/webpack");
|
|
114
|
+
// in the webpack config: plugins: [new GitShaPlugin()]
|
|
115
|
+
\`\`\`
|
|
116
|
+
- **Any other React setup**: add \`npx galadrim-feedback inject-git-sha\` to the build/deploy script, right after the build — it patches the built HTML files with the \`<meta name="git-sha">\` tag.
|
|
117
|
+
- **Docker builds**: the .git folder is usually absent from the build context, so pass the SHA explicitly: \`docker build --build-arg GIT_SHA=$(git rev-parse HEAD)\` with \`ARG GIT_SHA\` and \`ENV GIT_SHA=$GIT_SHA\` in the Dockerfile.
|
|
118
|
+
|
|
119
|
+
4. Configure FeedbackRoot component:
|
|
96
120
|
- Run \`pwd\` command in the frontend root directory and use this absolute path for rootDir prop
|
|
97
121
|
- Detect navigation package used in the project:
|
|
98
122
|
* If using React Router Dom (react-router-dom):
|
|
@@ -123,7 +147,7 @@ If your project uses Babel, you can use the Babel plugin instead.
|
|
|
123
147
|
- notionDatabaseId="${NOTION_DATABASE_ID}"
|
|
124
148
|
* Place it inside the router context but outside main content areas, if needed you can wrap it in a layout component that is parent to the rest of the app
|
|
125
149
|
|
|
126
|
-
|
|
150
|
+
5. Verification:
|
|
127
151
|
- Show all modified files
|
|
128
152
|
- Confirm the navigation setup works with the detected router
|
|
129
153
|
- List any manual steps needed (like getting projectId from admin) (see https://feedback.galadrim.ovh/admin)
|
|
@@ -138,15 +162,85 @@ Usage:
|
|
|
138
162
|
npx galadrim-feedback <command> [--project-id=<project-id>] [--notion-database-id=<notion-database-id>]
|
|
139
163
|
|
|
140
164
|
Commands:
|
|
141
|
-
cursor-init
|
|
142
|
-
|
|
165
|
+
cursor-init Send the installation prompt directly to Cursor/VS Code
|
|
166
|
+
inject-git-sha Inject the git commit SHA as <meta name="git-sha"> into
|
|
167
|
+
built HTML files (run after your build, before deploying).
|
|
168
|
+
Optional argument: output dir or HTML file (defaults to
|
|
169
|
+
dist/, build/ or out/)
|
|
170
|
+
help Show this help message
|
|
143
171
|
|
|
144
172
|
Examples:
|
|
145
173
|
npx galadrim-feedback cursor-init --project-id=<project-id> --notion-database-id=<notion-database-id>
|
|
174
|
+
npx galadrim-feedback inject-git-sha
|
|
175
|
+
npx galadrim-feedback inject-git-sha dist
|
|
146
176
|
npx galadrim-feedback help
|
|
147
177
|
`);
|
|
148
178
|
}
|
|
149
179
|
|
|
180
|
+
function collectHtmlFiles(target) {
|
|
181
|
+
if (statSync(target).isFile()) return [target];
|
|
182
|
+
const found = [];
|
|
183
|
+
const walk = (dir) => {
|
|
184
|
+
for (const entry of readdirSync(dir)) {
|
|
185
|
+
if (entry === "node_modules" || entry.startsWith(".")) continue;
|
|
186
|
+
const fullPath = join(dir, entry);
|
|
187
|
+
if (statSync(fullPath).isDirectory()) walk(fullPath);
|
|
188
|
+
else if (entry.endsWith(".html")) found.push(fullPath);
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
walk(target);
|
|
192
|
+
return found;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function injectMetaTag(html, sha) {
|
|
196
|
+
const meta = `<meta name="git-sha" content="${sha}">`;
|
|
197
|
+
const existing = /<meta\s+name=["']git-sha["'][^>]*\/?>/i;
|
|
198
|
+
if (existing.test(html)) return html.replace(existing, meta);
|
|
199
|
+
if (/<\/head>/i.test(html)) return html.replace(/<\/head>/i, `${meta}</head>`);
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function handleInjectGitSha(targetArg) {
|
|
204
|
+
const sha = resolveGitSha();
|
|
205
|
+
if (!sha) {
|
|
206
|
+
console.error(
|
|
207
|
+
"❌ Could not resolve the git SHA (no CI env var and `git rev-parse HEAD` failed).",
|
|
208
|
+
);
|
|
209
|
+
console.error(
|
|
210
|
+
"💡 In Docker builds, pass it in: --build-arg GIT_SHA=$(git rev-parse HEAD)",
|
|
211
|
+
);
|
|
212
|
+
process.exit(1);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const target =
|
|
216
|
+
targetArg || ["dist", "build", "out"].find((dir) => existsSync(dir));
|
|
217
|
+
if (!target || !existsSync(target)) {
|
|
218
|
+
console.error(
|
|
219
|
+
"❌ No build output found (tried dist/, build/, out/). Pass a path: npx galadrim-feedback inject-git-sha <dir-or-file>",
|
|
220
|
+
);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const htmlFiles = collectHtmlFiles(target);
|
|
225
|
+
let patched = 0;
|
|
226
|
+
for (const file of htmlFiles) {
|
|
227
|
+
const html = readFileSync(file, "utf8");
|
|
228
|
+
const updated = injectMetaTag(html, sha);
|
|
229
|
+
if (updated && updated !== html) {
|
|
230
|
+
writeFileSync(file, updated);
|
|
231
|
+
patched++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (patched === 0) {
|
|
236
|
+
console.error(`❌ No HTML file with a </head> tag found under "${target}".`);
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
console.log(
|
|
240
|
+
`✅ Injected git-sha ${sha.slice(0, 7)} into ${patched} HTML file(s) under "${target}"`,
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
150
244
|
function copyToClipboard(text) {
|
|
151
245
|
return new Promise((resolve, reject) => {
|
|
152
246
|
let copyCommand;
|
|
@@ -227,6 +321,9 @@ function main() {
|
|
|
227
321
|
case "cursor-init":
|
|
228
322
|
handleCursorInit();
|
|
229
323
|
break;
|
|
324
|
+
case "inject-git-sha":
|
|
325
|
+
handleInjectGitSha(args[1]);
|
|
326
|
+
break;
|
|
230
327
|
case "help":
|
|
231
328
|
case "--help":
|
|
232
329
|
case "-h":
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M12 16V12M12 8H12.01M22 12C22 17.5228 17.5228 22 12 22C6.47715 22 2 17.5228 2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12Z" stroke="#fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
|
3
|
+
</svg>
|