anproto personal data server
1const WIREDOVE_ORIGIN = 'http://localhost:8000'
2
3const applyWiredoveStyles = () => {
4 if (document.getElementById('anproto-wiredove-styles')) { return }
5 const style = document.createElement('style')
6 style.id = 'anproto-wiredove-styles'
7 style.textContent = `
8 .wiredove-share {
9 --wiredove-purple: #ac8aff;
10 display: inline-flex;
11 align-items: center;
12 gap: 4px;
13 padding: 0 0 0 5px;
14 height: 22px;
15 border-radius: 999px;
16 border: 1px solid var(--wiredove-purple);
17 color: #fff;
18 background: var(--wiredove-purple);
19 font-weight: 600;
20 font-size: 12px;
21 line-height: 1;
22 letter-spacing: 0.02em;
23 cursor: pointer;
24 }
25 .wiredove-share img {
26 width: 18px;
27 height: 18px;
28 display: block;
29 }
30 .wiredove-share:hover {
31 filter: brightness(0.95);
32 }
33 .wiredove-share-icon {
34 display: inline-flex;
35 align-items: center;
36 justify-content: center;
37 width: 32px;
38 height: 32px;
39 padding: 0;
40 border-radius: 0;
41 border: none;
42 background: transparent;
43 cursor: pointer;
44 }
45 .wiredove-share-icon img {
46 width: 28px;
47 height: 28px;
48 display: block;
49 }
50 .wiredove-share-icon:hover {
51 filter: brightness(0.9);
52 }
53 `
54 document.head.appendChild(style)
55}
56
57const ensureWiredoveLogo = (button, { createIfMissing = false } = {}) => {
58 let img = button.querySelector('img')
59 if (!img && createIfMissing) {
60 img = document.createElement('img')
61 button.appendChild(img)
62 }
63 if (!img) { return }
64 if (!img.getAttribute('alt')) { img.setAttribute('alt', 'Wiredove logo') }
65 if (!img.getAttribute('src')) {
66 img.src = new URL('./assets/dovepurple_sm.png', import.meta.url).href
67 }
68}
69
70const resolveValue = (value) => {
71 return typeof value === 'function' ? value() : value
72}
73
74const buildPayload = (value) => {
75 const base = {
76 title: document.title ? document.title.trim() : 'Shared link',
77 url: window.location.href
78 }
79 if (!value) { return base }
80 if (typeof value === 'string') {
81 const text = value.trim()
82 return text ? { ...base, text } : base
83 }
84 if (typeof value === 'object') {
85 return { ...base, ...value }
86 }
87 return base
88}
89
90const openWiredoveShare = (payload) => {
91 const encoded = encodeURIComponent(JSON.stringify(payload))
92 const target = `${WIREDOVE_ORIGIN}/#share=${encoded}`
93 window.open(target, '_blank', 'noopener')
94}
95
96export const attachShareButton = (button, payload) => {
97 if (!button) { return }
98 button.addEventListener('click', () => {
99 const resolved = resolveValue(payload)
100 openWiredoveShare(buildPayload(resolved))
101 })
102}
103
104export const attachWiredoveShareButton = (button, payload) => {
105 if (!button) { return }
106 applyWiredoveStyles()
107 button.classList.add('wiredove-share')
108 if (!button.getAttribute('title')) {
109 button.setAttribute('title', 'Share with ANProto')
110 }
111 ensureWiredoveLogo(button)
112 attachShareButton(button, payload)
113}
114
115export const attachWiredoveIconShareButton = (button, payload) => {
116 if (!button) { return }
117 applyWiredoveStyles()
118 button.classList.add('wiredove-share-icon')
119 if (!button.getAttribute('title')) {
120 button.setAttribute('title', 'Share with ANProto')
121 }
122 if (!button.getAttribute('aria-label')) {
123 button.setAttribute('aria-label', 'Share on ANProto')
124 }
125 ensureWiredoveLogo(button, { createIfMissing: true })
126 attachShareButton(button, payload)
127}