@soubiran/ui 0.0.0 → 0.1.2

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/dist/index.js ADDED
@@ -0,0 +1,876 @@
1
+ import { Fragment, Transition, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createTextVNode, createVNode, defineComponent, markRaw, mergeModels, normalizeClass, onMounted, openBlock, readonly, ref, renderList, renderSlot, toDisplayString, unref, useModel, withCtx } from "vue";
2
+ import { tv } from "tailwind-variants";
3
+ import UButton from "@nuxt/ui/components/Button.vue";
4
+ import UPopover from "@nuxt/ui/components/Popover.vue";
5
+ import { useRoute } from "vue-router";
6
+ import UCard from "@nuxt/ui/components/Card.vue";
7
+ import UFormField from "@nuxt/ui/components/FormField.vue";
8
+ import UIcon from "@nuxt/ui/components/Icon.vue";
9
+ import UTextarea from "@nuxt/ui/components/Textarea.vue";
10
+ import UTooltip from "@nuxt/ui/components/Tooltip.vue";
11
+ import { useMutation } from "@pinia/colada";
12
+ import { motion } from "motion-v";
13
+ import { ofetch } from "ofetch";
14
+ import { RadioGroupIndicator, RadioGroupItem, RadioGroupRoot } from "reka-ui";
15
+ import USeparator from "@nuxt/ui/components/Separator.vue";
16
+ import { createSharedComposable } from "@vueuse/core";
17
+ import PartySocket from "partysocket";
18
+
19
+ //#region src/components/Container.vue
20
+ const container = tv({ slots: {
21
+ base: "px-4 sm:px-6 lg:px-8",
22
+ inner: "w-full max-w-7xl mx-auto"
23
+ } });
24
+ const _sfc_main$10 = /* @__PURE__ */ defineComponent({
25
+ __name: "Container",
26
+ props: {
27
+ class: {},
28
+ ui: {}
29
+ },
30
+ setup(__props) {
31
+ const props = __props;
32
+ const ui = computed(() => container());
33
+ return (_ctx, _cache) => {
34
+ return openBlock(), createElementBlock("div", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [createElementVNode("div", { class: normalizeClass(ui.value.inner({ class: props.ui?.inner })) }, [renderSlot(_ctx.$slots, "default")], 2)], 2);
35
+ };
36
+ }
37
+ });
38
+ var Container_default = _sfc_main$10;
39
+
40
+ //#endregion
41
+ //#region ~icons/ph/thumbs-up
42
+ const _hoisted_1$11 = {
43
+ viewBox: "0 0 256 256",
44
+ width: "1.2em",
45
+ height: "1.2em"
46
+ };
47
+ function render$7(_ctx, _cache) {
48
+ return openBlock(), createElementBlock("svg", _hoisted_1$11, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
49
+ fill: "currentColor",
50
+ d: "M234 80.12A24 24 0 0 0 216 72h-56V56a40 40 0 0 0-40-40a8 8 0 0 0-7.16 4.42L75.06 96H32a16 16 0 0 0-16 16v88a16 16 0 0 0 16 16h172a24 24 0 0 0 23.82-21l12-96A24 24 0 0 0 234 80.12M32 112h40v88H32Zm191.94-15l-12 96a8 8 0 0 1-7.94 7H88v-94.11l36.71-73.43A24 24 0 0 1 144 56v24a8 8 0 0 0 8 8h64a8 8 0 0 1 7.94 9"
51
+ }, null, -1)])]);
52
+ }
53
+ var thumbs_up_default = markRaw({
54
+ name: "ph-thumbs-up",
55
+ render: render$7
56
+ });
57
+
58
+ //#endregion
59
+ //#region src/composables/useUmami.ts
60
+ function useUmami() {
61
+ const route = useRoute();
62
+ function track(event, data) {
63
+ window.umami?.track(event, {
64
+ ...data,
65
+ page_path: route.path
66
+ });
67
+ }
68
+ return { track };
69
+ }
70
+
71
+ //#endregion
72
+ //#region ~icons/ph/check-circle
73
+ const _hoisted_1$10 = {
74
+ viewBox: "0 0 256 256",
75
+ width: "1.2em",
76
+ height: "1.2em"
77
+ };
78
+ function render$6(_ctx, _cache) {
79
+ return openBlock(), createElementBlock("svg", _hoisted_1$10, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
80
+ fill: "currentColor",
81
+ d: "M173.66 98.34a8 8 0 0 1 0 11.32l-56 56a8 8 0 0 1-11.32 0l-24-24a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 0M232 128A104 104 0 1 1 128 24a104.11 104.11 0 0 1 104 104m-16 0a88 88 0 1 0-88 88a88.1 88.1 0 0 0 88-88"
82
+ }, null, -1)])]);
83
+ }
84
+ var check_circle_default = markRaw({
85
+ name: "ph-check-circle",
86
+ render: render$6
87
+ });
88
+
89
+ //#endregion
90
+ //#region src/components/FeedbackCard.vue
91
+ const _hoisted_1$9 = ["for"];
92
+ const feedbackCard = tv({ slots: {
93
+ base: "relative w-64",
94
+ successfullySentOverlay: "absolute z-10 inset-0 bg-default",
95
+ successfullySentContent: "absolute z-20 inset-0 flex flex-col justify-center items-center text-muted text-xs",
96
+ successfullySentIcon: "size-5",
97
+ input: "w-full",
98
+ radioGroup: "flex flex-row gap-2 text-lg",
99
+ radioGroupItem: "peer",
100
+ radioGroupLabel: "grayscale-100 hover:grayscale-0 peer-data-[state=checked]:grayscale-0"
101
+ } });
102
+ const _sfc_main$9 = /* @__PURE__ */ defineComponent({
103
+ __name: "FeedbackCard",
104
+ props: /* @__PURE__ */ mergeModels({
105
+ id: {},
106
+ class: {},
107
+ ui: {}
108
+ }, {
109
+ "content": { required: true },
110
+ "contentModifiers": {},
111
+ "rating": { required: true },
112
+ "ratingModifiers": {}
113
+ }),
114
+ emits: /* @__PURE__ */ mergeModels(["success"], ["update:content", "update:rating"]),
115
+ setup(__props, { emit: __emit }) {
116
+ const props = __props;
117
+ const emits = __emit;
118
+ const content = useModel(__props, "content");
119
+ const rating = useModel(__props, "rating");
120
+ const ratings = [
121
+ {
122
+ label: "😭",
123
+ value: "Hate it"
124
+ },
125
+ {
126
+ label: "🙁",
127
+ value: "Not great"
128
+ },
129
+ {
130
+ label: "🙂",
131
+ value: "It's ok"
132
+ },
133
+ {
134
+ label: "🤩",
135
+ value: "Love it"
136
+ }
137
+ ];
138
+ const successfullySent = ref(false);
139
+ const { track } = useUmami();
140
+ const { mutate, isLoading, error } = useMutation({
141
+ mutation: ({ rating: rating$1, content: content$1 }) => ofetch(`/api/posts/${props.id}/feedback`, {
142
+ method: "POST",
143
+ body: {
144
+ rating: rating$1,
145
+ content: content$1
146
+ },
147
+ baseURL: import.meta.env.VITE_API_BASE_URL
148
+ }),
149
+ onSuccess: () => {
150
+ successfullySent.value = true;
151
+ track("feedback_submit");
152
+ setTimeout(() => emits("success"), 200);
153
+ }
154
+ });
155
+ function sendFeedback() {
156
+ mutate({
157
+ rating: rating.value,
158
+ content: content.value
159
+ });
160
+ }
161
+ const formattedError = computed(() => {
162
+ if (!error.value) return void 0;
163
+ if (error.value.data?.errors) return error.value.data.errors.content?.[0] || error.value.data.errors.rating?.[0];
164
+ if (error.value.status === 404) return "Page not found. Cannot send feedback. It's us, not you!";
165
+ if (error.value.status === 503) return "Service is currently unavailable. Please try again later.";
166
+ return "An unexpected error occurred.";
167
+ });
168
+ const ui = computed(() => feedbackCard());
169
+ return (_ctx, _cache) => {
170
+ return openBlock(), createBlock(UCard, {
171
+ class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })),
172
+ ui: {
173
+ body: "p-2 sm:p-2",
174
+ footer: "p-2 sm:p-2 flex items-center justify-between"
175
+ }
176
+ }, {
177
+ footer: withCtx(() => [createVNode(unref(RadioGroupRoot), {
178
+ modelValue: rating.value,
179
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => rating.value = $event),
180
+ class: normalizeClass(ui.value.radioGroup({ class: props.ui?.radioGroup }))
181
+ }, {
182
+ default: withCtx(() => [(openBlock(), createElementBlock(Fragment, null, renderList(ratings, (item) => {
183
+ return createElementVNode("div", { key: item.value }, [createVNode(unref(RadioGroupItem), {
184
+ id: item.value,
185
+ value: item.value,
186
+ class: normalizeClass(ui.value.radioGroupItem({ class: props.ui?.radioGroupItem }))
187
+ }, {
188
+ default: withCtx(() => [createVNode(unref(RadioGroupIndicator))]),
189
+ _: 1
190
+ }, 8, [
191
+ "id",
192
+ "value",
193
+ "class"
194
+ ]), createVNode(UTooltip, { text: item.value }, {
195
+ default: withCtx(() => [createElementVNode("label", {
196
+ for: item.value,
197
+ class: normalizeClass(ui.value.radioGroupLabel({ class: props.ui?.radioGroupLabel }))
198
+ }, toDisplayString(item.label), 11, _hoisted_1$9)]),
199
+ _: 2
200
+ }, 1032, ["text"])]);
201
+ }), 64))]),
202
+ _: 1
203
+ }, 8, ["modelValue", "class"]), createVNode(UButton, {
204
+ size: "sm",
205
+ label: "Send",
206
+ loading: unref(isLoading),
207
+ onClick: sendFeedback
208
+ }, null, 8, ["loading"])]),
209
+ default: withCtx(() => [successfullySent.value ? (openBlock(), createElementBlock(Fragment, { key: 0 }, [createVNode(unref(motion).div, {
210
+ initial: { opacity: 0 },
211
+ animate: {
212
+ opacity: 1,
213
+ transition: { duration: .2 }
214
+ },
215
+ class: normalizeClass(ui.value.successfullySentOverlay({ class: props.ui?.successfullySentOverlay }))
216
+ }, null, 8, ["class"]), createElementVNode("div", { class: normalizeClass(ui.value.successfullySentContent({ class: props.ui?.successfullySentContent })) }, [
217
+ createVNode(unref(motion).div, {
218
+ initial: {
219
+ opacity: 0,
220
+ transform: "translateY(4px)"
221
+ },
222
+ animate: {
223
+ opacity: 1,
224
+ transform: "translateY(0)",
225
+ transition: {
226
+ delay: .1,
227
+ duration: .3
228
+ }
229
+ }
230
+ }, {
231
+ default: withCtx(() => [createVNode(UIcon, {
232
+ name: unref(check_circle_default),
233
+ class: normalizeClass(ui.value.successfullySentIcon({ class: props.ui?.successfullySentIcon }))
234
+ }, null, 8, ["name", "class"])]),
235
+ _: 1
236
+ }),
237
+ createVNode(unref(motion).p, {
238
+ initial: {
239
+ opacity: 0,
240
+ transform: "translateY(4px)"
241
+ },
242
+ animate: {
243
+ opacity: 1,
244
+ transform: "translateY(0)",
245
+ transition: {
246
+ delay: .2,
247
+ duration: .3
248
+ }
249
+ },
250
+ class: "mt-3"
251
+ }, {
252
+ default: withCtx(() => [..._cache[2] || (_cache[2] = [createTextVNode(" Your feedback has been received. ", -1)])]),
253
+ _: 1
254
+ }),
255
+ createVNode(unref(motion).p, {
256
+ initial: {
257
+ opacity: 0,
258
+ transform: "translateY(4px)"
259
+ },
260
+ animate: {
261
+ opacity: 1,
262
+ transform: "translateY(0)",
263
+ transition: {
264
+ delay: .3,
265
+ duration: .3
266
+ }
267
+ },
268
+ class: "mt-1"
269
+ }, {
270
+ default: withCtx(() => [..._cache[3] || (_cache[3] = [createTextVNode(" Thanks for your help! ", -1)])]),
271
+ _: 1
272
+ })
273
+ ], 2)], 64)) : createCommentVNode("v-if", true), createVNode(UFormField, { error: formattedError.value }, {
274
+ error: withCtx(({ error: formFieldError }) => [formFieldError ? (openBlock(), createBlock(unref(motion).div, {
275
+ key: 0,
276
+ initial: { height: 0 },
277
+ animate: { height: "auto" },
278
+ class: "text-sm"
279
+ }, {
280
+ default: withCtx(() => [createTextVNode(toDisplayString(formFieldError), 1)]),
281
+ _: 2
282
+ }, 1024)) : createCommentVNode("v-if", true)]),
283
+ default: withCtx(() => [createVNode(UTextarea, {
284
+ modelValue: content.value,
285
+ "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => content.value = $event),
286
+ placeholder: "Your feedback...",
287
+ variant: "soft",
288
+ class: normalizeClass(ui.value.input({ class: props.ui?.input }))
289
+ }, null, 8, ["modelValue", "class"])]),
290
+ _: 1
291
+ }, 8, ["error"])]),
292
+ _: 1
293
+ }, 8, ["class"]);
294
+ };
295
+ }
296
+ });
297
+ var FeedbackCard_default = _sfc_main$9;
298
+
299
+ //#endregion
300
+ //#region src/components/Feedback.vue
301
+ const feedback = tv({ slots: { base: "px-0 py-1 text-dimmed text-sm" } });
302
+ const _sfc_main$8 = /* @__PURE__ */ defineComponent({
303
+ __name: "Feedback",
304
+ props: {
305
+ id: {},
306
+ class: {},
307
+ ui: {}
308
+ },
309
+ setup(__props) {
310
+ const props = __props;
311
+ const { track } = useUmami();
312
+ function onClick() {
313
+ track("feedback_click");
314
+ }
315
+ const content = ref("");
316
+ const rating = ref("");
317
+ function onSuccess() {
318
+ content.value = "";
319
+ rating.value = "";
320
+ }
321
+ const ui = computed(() => feedback());
322
+ return (_ctx, _cache) => {
323
+ return openBlock(), createBlock(UPopover, { ui: { content: "ring-0 data-[state=open]:animate-[scale-up_100ms_ease-out]" } }, {
324
+ content: withCtx(() => [createVNode(FeedbackCard_default, {
325
+ id: props.id,
326
+ rating: rating.value,
327
+ "onUpdate:rating": _cache[0] || (_cache[0] = ($event) => rating.value = $event),
328
+ content: content.value,
329
+ "onUpdate:content": _cache[1] || (_cache[1] = ($event) => content.value = $event),
330
+ onSuccess
331
+ }, null, 8, [
332
+ "id",
333
+ "rating",
334
+ "content"
335
+ ])]),
336
+ default: withCtx(() => [createVNode(UButton, {
337
+ variant: "link",
338
+ color: "neutral",
339
+ label: "Give feedback",
340
+ size: "sm",
341
+ icon: unref(thumbs_up_default),
342
+ class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })),
343
+ onClick
344
+ }, null, 8, ["icon", "class"])]),
345
+ _: 1
346
+ });
347
+ };
348
+ }
349
+ });
350
+ var Feedback_default = _sfc_main$8;
351
+
352
+ //#endregion
353
+ //#region ~icons/simple-icons/discord
354
+ const _hoisted_1$8 = {
355
+ viewBox: "0 0 24 24",
356
+ width: "1.2em",
357
+ height: "1.2em"
358
+ };
359
+ function render$5(_ctx, _cache) {
360
+ return openBlock(), createElementBlock("svg", _hoisted_1$8, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
361
+ fill: "currentColor",
362
+ d: "M20.317 4.37a19.8 19.8 0 0 0-4.885-1.515a.074.074 0 0 0-.079.037c-.21.375-.444.864-.608 1.25a18.3 18.3 0 0 0-5.487 0a13 13 0 0 0-.617-1.25a.08.08 0 0 0-.079-.037A19.7 19.7 0 0 0 3.677 4.37a.1.1 0 0 0-.032.027C.533 9.046-.32 13.58.099 18.057a.08.08 0 0 0 .031.057a19.9 19.9 0 0 0 5.993 3.03a.08.08 0 0 0 .084-.028a14 14 0 0 0 1.226-1.994a.076.076 0 0 0-.041-.106a13 13 0 0 1-1.872-.892a.077.077 0 0 1-.008-.128a10 10 0 0 0 .372-.292a.07.07 0 0 1 .077-.01c3.928 1.793 8.18 1.793 12.062 0a.07.07 0 0 1 .078.01q.181.149.373.292a.077.077 0 0 1-.006.127a12.3 12.3 0 0 1-1.873.892a.077.077 0 0 0-.041.107c.36.698.772 1.362 1.225 1.993a.08.08 0 0 0 .084.028a19.8 19.8 0 0 0 6.002-3.03a.08.08 0 0 0 .032-.054c.5-5.177-.838-9.674-3.549-13.66a.06.06 0 0 0-.031-.03M8.02 15.33c-1.182 0-2.157-1.085-2.157-2.419c0-1.333.956-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.956 2.418-2.157 2.418m7.975 0c-1.183 0-2.157-1.085-2.157-2.419c0-1.333.955-2.419 2.157-2.419c1.21 0 2.176 1.096 2.157 2.42c0 1.333-.946 2.418-2.157 2.418"
363
+ }, null, -1)])]);
364
+ }
365
+ var discord_default = markRaw({
366
+ name: "simple-icons-discord",
367
+ render: render$5
368
+ });
369
+
370
+ //#endregion
371
+ //#region ~icons/simple-icons/github
372
+ const _hoisted_1$7 = {
373
+ viewBox: "0 0 24 24",
374
+ width: "1.2em",
375
+ height: "1.2em"
376
+ };
377
+ function render$4(_ctx, _cache) {
378
+ return openBlock(), createElementBlock("svg", _hoisted_1$7, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
379
+ fill: "currentColor",
380
+ d: "M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
381
+ }, null, -1)])]);
382
+ }
383
+ var github_default = markRaw({
384
+ name: "simple-icons-github",
385
+ render: render$4
386
+ });
387
+
388
+ //#endregion
389
+ //#region ~icons/simple-icons/linkedin
390
+ const _hoisted_1$6 = {
391
+ viewBox: "0 0 24 24",
392
+ width: "1.2em",
393
+ height: "1.2em"
394
+ };
395
+ function render$3(_ctx, _cache) {
396
+ return openBlock(), createElementBlock("svg", _hoisted_1$6, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
397
+ fill: "currentColor",
398
+ d: "M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z"
399
+ }, null, -1)])]);
400
+ }
401
+ var linkedin_default = markRaw({
402
+ name: "simple-icons-linkedin",
403
+ render: render$3
404
+ });
405
+
406
+ //#endregion
407
+ //#region ~icons/simple-icons/twitch
408
+ const _hoisted_1$5 = {
409
+ viewBox: "0 0 24 24",
410
+ width: "1.2em",
411
+ height: "1.2em"
412
+ };
413
+ function render$2(_ctx, _cache) {
414
+ return openBlock(), createElementBlock("svg", _hoisted_1$5, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
415
+ fill: "currentColor",
416
+ d: "M11.571 4.714h1.715v5.143H11.57zm4.715 0H18v5.143h-1.714zM6 0L1.714 4.286v15.428h5.143V24l4.286-4.286h3.428L22.286 12V0zm14.571 11.143l-3.428 3.428h-3.429l-3 3v-3H6.857V1.714h13.714Z"
417
+ }, null, -1)])]);
418
+ }
419
+ var twitch_default = markRaw({
420
+ name: "simple-icons-twitch",
421
+ render: render$2
422
+ });
423
+
424
+ //#endregion
425
+ //#region ~icons/simple-icons/x
426
+ const _hoisted_1$4 = {
427
+ viewBox: "0 0 24 24",
428
+ width: "1.2em",
429
+ height: "1.2em"
430
+ };
431
+ function render$1(_ctx, _cache) {
432
+ return openBlock(), createElementBlock("svg", _hoisted_1$4, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
433
+ fill: "currentColor",
434
+ d: "M14.234 10.162L22.977 0h-2.072l-7.591 8.824L7.251 0H.258l9.168 13.343L.258 24H2.33l8.016-9.318L16.749 24h6.993zm-2.837 3.299l-.929-1.329L3.076 1.56h3.182l5.965 8.532l.929 1.329l7.754 11.09h-3.182z"
435
+ }, null, -1)])]);
436
+ }
437
+ var x_default = markRaw({
438
+ name: "simple-icons-x",
439
+ render: render$1
440
+ });
441
+
442
+ //#endregion
443
+ //#region src/components/Header.vue
444
+ const header = tv({ slots: {
445
+ base: "h-(--ui-header-height) flex flex-row gap-4 items-center justify-end",
446
+ link: "p-0 text-dimmed"
447
+ } });
448
+ const _sfc_main$7 = /* @__PURE__ */ defineComponent({
449
+ __name: "Header",
450
+ props: {
451
+ links: {},
452
+ class: {},
453
+ ui: {}
454
+ },
455
+ setup(__props) {
456
+ const props = __props;
457
+ const { track } = useUmami();
458
+ function trackClick(label) {
459
+ track("header_click", { label });
460
+ }
461
+ const ui = computed(() => header());
462
+ return (_ctx, _cache) => {
463
+ return openBlock(), createBlock(Container_default, { ui: { inner: "max-w-5xl" } }, {
464
+ default: withCtx(() => [createElementVNode("header", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [
465
+ (openBlock(true), createElementBlock(Fragment, null, renderList(props.links, (link) => {
466
+ return openBlock(), createBlock(UTooltip, {
467
+ key: link.label,
468
+ text: link.label
469
+ }, {
470
+ default: withCtx(() => [createVNode(UButton, {
471
+ variant: "link",
472
+ color: "neutral",
473
+ to: link.to,
474
+ "aria-label": link.label,
475
+ icon: link.icon,
476
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
477
+ onClick: ($event) => trackClick(link.label)
478
+ }, null, 8, [
479
+ "to",
480
+ "aria-label",
481
+ "icon",
482
+ "class",
483
+ "onClick"
484
+ ])]),
485
+ _: 2
486
+ }, 1032, ["text"]);
487
+ }), 128)),
488
+ createVNode(USeparator, {
489
+ orientation: "vertical",
490
+ class: "h-5"
491
+ }),
492
+ createVNode(UTooltip, { text: "LinkedIn" }, {
493
+ default: withCtx(() => [createVNode(UButton, {
494
+ href: "https://www.linkedin.com/in/esteban25/",
495
+ target: "_blank",
496
+ variant: "link",
497
+ color: "neutral",
498
+ "aria-label": "LinkedIn",
499
+ icon: unref(linkedin_default),
500
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
501
+ ui: { leadingIcon: "size-4" },
502
+ onClick: _cache[0] || (_cache[0] = ($event) => trackClick("LinkedIn"))
503
+ }, null, 8, ["icon", "class"])]),
504
+ _: 1
505
+ }),
506
+ createVNode(UTooltip, { text: "X" }, {
507
+ default: withCtx(() => [createVNode(UButton, {
508
+ href: "https://x.com/soubiran_",
509
+ target: "_blank",
510
+ variant: "link",
511
+ color: "neutral",
512
+ "aria-label": "X",
513
+ icon: unref(x_default),
514
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
515
+ ui: { leadingIcon: "size-4" },
516
+ onClick: _cache[1] || (_cache[1] = ($event) => trackClick("X"))
517
+ }, null, 8, ["icon", "class"])]),
518
+ _: 1
519
+ }),
520
+ createVNode(UTooltip, { text: "Twitch" }, {
521
+ default: withCtx(() => [createVNode(UButton, {
522
+ href: "https://www.twitch.tv/barbapapazes",
523
+ target: "_blank",
524
+ variant: "link",
525
+ color: "neutral",
526
+ "aria-label": "Twitch",
527
+ icon: unref(twitch_default),
528
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
529
+ ui: { leadingIcon: "size-4" },
530
+ onClick: _cache[2] || (_cache[2] = ($event) => trackClick("Twitch"))
531
+ }, null, 8, ["icon", "class"])]),
532
+ _: 1
533
+ }),
534
+ createVNode(UTooltip, { text: "GitHub" }, {
535
+ default: withCtx(() => [createVNode(UButton, {
536
+ href: "https://github.com/barbapapazes",
537
+ target: "_blank",
538
+ variant: "link",
539
+ color: "neutral",
540
+ "aria-label": "GitHub",
541
+ icon: unref(github_default),
542
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
543
+ ui: { leadingIcon: "size-4" },
544
+ onClick: _cache[3] || (_cache[3] = ($event) => trackClick("GitHub"))
545
+ }, null, 8, ["icon", "class"])]),
546
+ _: 1
547
+ }),
548
+ createVNode(UTooltip, { text: "Discord" }, {
549
+ default: withCtx(() => [createVNode(UButton, {
550
+ href: "https://discord.gg/q2ghCGUuFR",
551
+ target: "_blank",
552
+ variant: "link",
553
+ color: "neutral",
554
+ "aria-label": "Discord",
555
+ icon: unref(discord_default),
556
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
557
+ ui: { leadingIcon: "size-4" },
558
+ onClick: _cache[4] || (_cache[4] = ($event) => trackClick("Discord"))
559
+ }, null, 8, ["icon", "class"])]),
560
+ _: 1
561
+ })
562
+ ], 2)]),
563
+ _: 1
564
+ });
565
+ };
566
+ }
567
+ });
568
+ var Header_default = _sfc_main$7;
569
+
570
+ //#endregion
571
+ //#region src/components/Page.vue
572
+ const page = tv({ slots: {
573
+ base: "py-12",
574
+ header: "max-w-3xl mx-auto xl:max-w-none xl:grid xl:grid-cols-[256px_768px_256px]",
575
+ headerInner: "xl:col-start-2",
576
+ content: "mt-6 max-w-3xl mx-auto xl:max-w-none xl:grid xl:grid-cols-[256px_768px_256px] xl:mx-auto",
577
+ contentInner: "xl:col-start-2",
578
+ right: "hidden xl:block xl:col-start-3 xl:pl-8 xl:h-full",
579
+ rightInner: "sticky top-4"
580
+ } });
581
+ const _sfc_main$6 = /* @__PURE__ */ defineComponent({
582
+ __name: "Page",
583
+ props: {
584
+ class: {},
585
+ ui: {}
586
+ },
587
+ setup(__props) {
588
+ const props = __props;
589
+ const ui = computed(() => page());
590
+ return (_ctx, _cache) => {
591
+ return openBlock(), createElementBlock("div", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [createVNode(Container_default, null, {
592
+ default: withCtx(() => [createElementVNode("div", { class: normalizeClass(ui.value.header({ class: props.ui?.header })) }, [createElementVNode("div", { class: normalizeClass(ui.value.headerInner({ class: props.ui?.headerInner })) }, [renderSlot(_ctx.$slots, "header")], 2)], 2), createElementVNode("div", { class: normalizeClass(ui.value.content({ class: props.ui?.content })) }, [createElementVNode("div", { class: normalizeClass(ui.value.contentInner({ class: props.ui?.contentInner })) }, [renderSlot(_ctx.$slots, "default")], 2), createElementVNode("div", { class: normalizeClass(ui.value.right({ class: props.ui?.right })) }, [createElementVNode("div", { class: normalizeClass(ui.value.rightInner({ class: props.ui?.rightInner })) }, [renderSlot(_ctx.$slots, "right")], 2)], 2)], 2)]),
593
+ _: 3
594
+ }), renderSlot(_ctx.$slots, "bottom")], 2);
595
+ };
596
+ }
597
+ });
598
+ var Page_default = _sfc_main$6;
599
+
600
+ //#endregion
601
+ //#region src/components/PageTitle.vue
602
+ const pageTitle = tv({ slots: { base: "text-xl font-bold text-highlighted" } });
603
+ const _sfc_main$5 = /* @__PURE__ */ defineComponent({
604
+ __name: "PageTitle",
605
+ props: {
606
+ title: {},
607
+ class: {},
608
+ ui: {}
609
+ },
610
+ setup(__props) {
611
+ const props = __props;
612
+ const ui = computed(() => pageTitle());
613
+ return (_ctx, _cache) => {
614
+ return openBlock(), createElementBlock("h1", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, toDisplayString(__props.title), 3);
615
+ };
616
+ }
617
+ });
618
+ var PageTitle_default = _sfc_main$5;
619
+
620
+ //#endregion
621
+ //#region src/components/PageHeader.vue
622
+ const pageHeader = tv({ slots: { base: "" } });
623
+ const _sfc_main$4 = /* @__PURE__ */ defineComponent({
624
+ __name: "PageHeader",
625
+ props: {
626
+ title: {},
627
+ class: {},
628
+ ui: {}
629
+ },
630
+ setup(__props) {
631
+ const props = __props;
632
+ const ui = computed(() => pageHeader());
633
+ return (_ctx, _cache) => {
634
+ return openBlock(), createElementBlock("div", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [createVNode(PageTitle_default, { title: props.title }, null, 8, ["title"]), renderSlot(_ctx.$slots, "after")], 2);
635
+ };
636
+ }
637
+ });
638
+ var PageHeader_default = _sfc_main$4;
639
+
640
+ //#endregion
641
+ //#region ~icons/simple-icons/youtube
642
+ const _hoisted_1$3 = {
643
+ viewBox: "0 0 24 24",
644
+ width: "1.2em",
645
+ height: "1.2em"
646
+ };
647
+ function render(_ctx, _cache) {
648
+ return openBlock(), createElementBlock("svg", _hoisted_1$3, [..._cache[0] || (_cache[0] = [createElementVNode("path", {
649
+ fill: "currentColor",
650
+ d: "M23.498 6.186a3.02 3.02 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.02 3.02 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.02 3.02 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.02 3.02 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814M9.545 15.568V8.432L15.818 12z"
651
+ }, null, -1)])]);
652
+ }
653
+ var youtube_default = markRaw({
654
+ name: "simple-icons-youtube",
655
+ render
656
+ });
657
+
658
+ //#endregion
659
+ //#region src/components/Socials.vue
660
+ const socials = tv({ slots: {
661
+ base: "flex flex-wrap gap-2",
662
+ link: "flex items-center gap-1",
663
+ icon: "size-4"
664
+ } });
665
+ const _sfc_main$3 = /* @__PURE__ */ defineComponent({
666
+ __name: "Socials",
667
+ props: {
668
+ class: {},
669
+ ui: {}
670
+ },
671
+ setup(__props) {
672
+ const props = __props;
673
+ const ui = computed(() => socials());
674
+ return (_ctx, _cache) => {
675
+ return openBlock(), createElementBlock("p", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [
676
+ createElementVNode("a", {
677
+ href: "https://www.linkedin.com/in/esteban25/",
678
+ rel: "noopener noreferrer",
679
+ target: "_blank",
680
+ class: normalizeClass(ui.value.link({ class: props.ui?.link }))
681
+ }, [createVNode(UIcon, {
682
+ name: unref(linkedin_default),
683
+ class: normalizeClass(ui.value.icon({ class: props.ui?.icon }))
684
+ }, null, 8, ["name", "class"]), _cache[0] || (_cache[0] = createElementVNode("span", null, "LinkedIn", -1))], 2),
685
+ createElementVNode("a", {
686
+ href: "https://x.com/soubiran_",
687
+ rel: "noopener noreferrer",
688
+ target: "_blank",
689
+ class: normalizeClass(ui.value.link({ class: props.ui?.link }))
690
+ }, [createVNode(UIcon, {
691
+ name: unref(x_default),
692
+ class: normalizeClass(ui.value.icon({ class: props.ui?.icon }))
693
+ }, null, 8, ["name", "class"]), _cache[1] || (_cache[1] = createElementVNode("span", null, "X (Twitter)", -1))], 2),
694
+ createElementVNode("a", {
695
+ href: "https://github.com/barbapapazes",
696
+ rel: "noopener noreferrer",
697
+ target: "_blank",
698
+ class: normalizeClass(ui.value.link({ class: props.ui?.link }))
699
+ }, [createVNode(UIcon, {
700
+ name: unref(github_default),
701
+ class: normalizeClass(ui.value.icon({ class: props.ui?.icon }))
702
+ }, null, 8, ["name", "class"]), _cache[2] || (_cache[2] = createElementVNode("span", null, "GitHub", -1))], 2),
703
+ createElementVNode("a", {
704
+ href: "https://www.twitch.tv/barbapapazes",
705
+ rel: "noopener noreferrer",
706
+ target: "_blank",
707
+ class: normalizeClass(ui.value.link({ class: props.ui?.link }))
708
+ }, [createVNode(UIcon, {
709
+ name: unref(twitch_default),
710
+ class: normalizeClass(ui.value.icon({ class: props.ui?.icon }))
711
+ }, null, 8, ["name", "class"]), _cache[3] || (_cache[3] = createElementVNode("span", null, "Twitch", -1))], 2),
712
+ createElementVNode("a", {
713
+ href: "https://www.youtube.com/@barbapapazes",
714
+ target: "_blank",
715
+ rel: "noopener noreferrer",
716
+ class: normalizeClass(ui.value.link({ class: props.ui?.link }))
717
+ }, [createVNode(UIcon, {
718
+ name: unref(youtube_default),
719
+ class: normalizeClass(ui.value.icon({ class: props.ui?.icon }))
720
+ }, null, 8, ["name", "class"]), _cache[4] || (_cache[4] = createElementVNode("span", null, "YouTube", -1))], 2)
721
+ ], 2);
722
+ };
723
+ }
724
+ });
725
+ var Socials_default = _sfc_main$3;
726
+
727
+ //#endregion
728
+ //#region src/components/Sponsors.vue
729
+ const _hoisted_1$2 = {
730
+ href: "https://soubiran.dev/sponsorship?utm_source=infra.soubiran.dev&utm_medium=link",
731
+ target: "_blank"
732
+ };
733
+ const sponsors = tv({ slots: {
734
+ base: "not-prose",
735
+ img: "border-0"
736
+ } });
737
+ const _sfc_main$2 = /* @__PURE__ */ defineComponent({
738
+ __name: "Sponsors",
739
+ props: {
740
+ class: {},
741
+ ui: {}
742
+ },
743
+ setup(__props) {
744
+ const props = __props;
745
+ const ui = computed(() => sponsors());
746
+ return (_ctx, _cache) => {
747
+ return openBlock(), createElementBlock("p", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [createElementVNode("a", _hoisted_1$2, [createElementVNode("img", {
748
+ src: "https://raw.githubusercontent.com/Barbapapazes/static/refs/heads/main/sponsors.svg",
749
+ alt: "Sponsors",
750
+ class: normalizeClass(ui.value.img({ class: props.ui?.img }))
751
+ }, null, 2)])], 2);
752
+ };
753
+ }
754
+ });
755
+ var Sponsors_default = _sfc_main$2;
756
+
757
+ //#endregion
758
+ //#region src/composables/useTableOfContents.ts
759
+ function _useTableOfContents() {
760
+ const activeHeadings = ref([]);
761
+ function setActive(id) {
762
+ activeHeadings.value.push(id);
763
+ }
764
+ function unsetActive(id) {
765
+ activeHeadings.value = activeHeadings.value.filter((h) => h !== id);
766
+ }
767
+ return {
768
+ setActive,
769
+ unsetActive,
770
+ activeHeadings: readonly(activeHeadings)
771
+ };
772
+ }
773
+ var useTableOfContents_default = createSharedComposable(_useTableOfContents);
774
+
775
+ //#endregion
776
+ //#region src/components/TableOfContents.vue
777
+ const _hoisted_1$1 = [
778
+ "href",
779
+ "data-active",
780
+ "onClick"
781
+ ];
782
+ const tableOfContents = tv({ slots: {
783
+ base: "space-y-1 text-sm text-dimmed font-medium",
784
+ title: "",
785
+ list: "font-sofia",
786
+ link: "flex gap-1 px-0 py-1 text-dimmed hover:text-default active:text-default transition-colors focus:outline-none focus-visible:ring-inset focus-visible:ring-2 focus-visible:ring-inverted data-active:text-default rounded-md"
787
+ } });
788
+ const _sfc_main$1 = /* @__PURE__ */ defineComponent({
789
+ __name: "TableOfContents",
790
+ props: {
791
+ toc: {},
792
+ class: {},
793
+ ui: {}
794
+ },
795
+ setup(__props) {
796
+ const props = __props;
797
+ const { track } = useUmami();
798
+ function trackClick(text, hash) {
799
+ track("table_of_contents_click", {
800
+ toc_text: text,
801
+ toc_hash: hash
802
+ });
803
+ }
804
+ const { activeHeadings } = useTableOfContents_default();
805
+ const ui = computed(() => tableOfContents());
806
+ return (_ctx, _cache) => {
807
+ return openBlock(), createElementBlock("div", { class: normalizeClass(ui.value.base({ class: [props.ui?.base, props.class] })) }, [createElementVNode("div", { class: normalizeClass(ui.value.title({ class: props.ui?.title })) }, " Table of Contents ", 2), createElementVNode("ul", { class: normalizeClass(ui.value.list({ class: props.ui?.list })) }, [(openBlock(true), createElementBlock(Fragment, null, renderList(props.toc, (item, index) => {
808
+ return openBlock(), createElementBlock("li", { key: item.anchor || index }, [createCommentVNode(" Cannot use RouterLink as the anchor will be prefixed with the route path. "), item.anchor ? (openBlock(), createElementBlock("a", {
809
+ key: 0,
810
+ custom: "",
811
+ variant: "link",
812
+ color: "neutral",
813
+ href: `#${item.anchor}`,
814
+ "data-active": unref(activeHeadings).includes(item.anchor || "") ? true : void 0,
815
+ class: normalizeClass(ui.value.link({ class: props.ui?.link })),
816
+ onClick: ($event) => trackClick(item.text, item.anchor)
817
+ }, [createElementVNode("span", null, toDisplayString(index + 1) + ".", 1), createTextVNode(" " + toDisplayString(item.text), 1)], 10, _hoisted_1$1)) : createCommentVNode("v-if", true)]);
818
+ }), 128))], 2)], 2);
819
+ };
820
+ }
821
+ });
822
+ var TableOfContents_default = _sfc_main$1;
823
+
824
+ //#endregion
825
+ //#region src/components/ViewersCounter.vue
826
+ const _hoisted_1 = ["title"];
827
+ const viewersCounter = tv({ slots: {
828
+ base: "flex items-center justify-center gap-[0.375rem] border border-green-400 rounded-full px-[0.375rem] py-[0.125rem] text-xs text-muted font-light leading-3 dark:border-green-700",
829
+ dot: "inline-block h-[0.375rem] w-[0.375rem] animate-[pulse_4s_cubic-bezier(0.4,_0,_0.6,_1)_infinite] rounded-full bg-green-400 ring ring-2 ring-green-200 dark:bg-green-700 dark:ring-green-500"
830
+ } });
831
+ const _sfc_main = /* @__PURE__ */ defineComponent({
832
+ __name: "ViewersCounter",
833
+ props: {
834
+ class: {},
835
+ ui: {}
836
+ },
837
+ setup(__props) {
838
+ const props = __props;
839
+ const count = ref(0);
840
+ const connections = ref({});
841
+ function connect() {
842
+ return new PartySocket({
843
+ host: import.meta.env.VITE_PARTYKIT_URL,
844
+ room: "soubiran-dev"
845
+ });
846
+ }
847
+ onMounted(() => {
848
+ connect().addEventListener("message", (event) => {
849
+ const data = JSON.parse(event.data);
850
+ count.value = data.count;
851
+ connections.value = data.connections;
852
+ });
853
+ });
854
+ const title = computed(() => {
855
+ return `${count.value} viewer${count.value > 1 ? "s" : ""} currently online (${Object.keys(connections.value).map((host) => `${connections.value[host]} from ${host}`).join(", ")})`;
856
+ });
857
+ const ui = computed(() => viewersCounter());
858
+ return (_ctx, _cache) => {
859
+ return openBlock(), createBlock(Transition, { name: "fade" }, {
860
+ default: withCtx(() => [count.value ? (openBlock(), createElementBlock("div", {
861
+ key: 0,
862
+ title: title.value,
863
+ class: normalizeClass(ui.value.base({ class: [props.class, props.ui?.base] }))
864
+ }, [createElementVNode("span", null, toDisplayString(count.value), 1), createElementVNode("span", {
865
+ class: normalizeClass(ui.value.dot({ class: props.ui?.dot })),
866
+ "aria-hidden": "true"
867
+ }, null, 2)], 10, _hoisted_1)) : createCommentVNode("v-if", true)]),
868
+ _: 1
869
+ });
870
+ };
871
+ }
872
+ });
873
+ var ViewersCounter_default = _sfc_main;
874
+
875
+ //#endregion
876
+ export { Container_default as Container, Feedback_default as Feedback, FeedbackCard_default as FeedbackCard, Header_default as Header, Page_default as Page, PageHeader_default as PageHeader, PageTitle_default as PageTitle, Socials_default as Socials, Sponsors_default as Sponsors, TableOfContents_default as TableOfContents, ViewersCounter_default as ViewersCounter, useTableOfContents_default as useTableOfContents, useUmami };