fedipod 0.8.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.
- package/LICENSE +21 -0
- package/README.md +123 -0
- package/architecture.svg +89 -0
- package/bin/fedipod.mjs +2303 -0
- package/cli.md +243 -0
- package/groups.md +85 -0
- package/gui.md +99 -0
- package/lib/account.mjs +87 -0
- package/lib/admin.mjs +1606 -0
- package/lib/atproto.mjs +310 -0
- package/lib/bskyfeed.mjs +166 -0
- package/lib/bskygroup.mjs +162 -0
- package/lib/c2s.mjs +305 -0
- package/lib/certs.mjs +265 -0
- package/lib/deliver.mjs +249 -0
- package/lib/directory.mjs +90 -0
- package/lib/embed.mjs +214 -0
- package/lib/export-collections.mjs +161 -0
- package/lib/front-core.mjs +322 -0
- package/lib/gateway-core.mjs +115 -0
- package/lib/guard.mjs +213 -0
- package/lib/home.mjs +161 -0
- package/lib/httpsig.mjs +109 -0
- package/lib/import.mjs +357 -0
- package/lib/intake.mjs +1710 -0
- package/lib/keys.mjs +139 -0
- package/lib/lease.mjs +190 -0
- package/lib/localapi.mjs +87 -0
- package/lib/mastoapi.mjs +1588 -0
- package/lib/migrate.mjs +127 -0
- package/lib/oidc-auth.mjs +53 -0
- package/lib/podrdf.mjs +132 -0
- package/lib/ports.mjs +27 -0
- package/lib/publisher.mjs +1003 -0
- package/lib/remote.mjs +303 -0
- package/lib/safefetch.mjs +235 -0
- package/lib/setup.mjs +262 -0
- package/lib/social.mjs +505 -0
- package/lib/storage.mjs +170 -0
- package/lib/store.mjs +470 -0
- package/lib/streaming.mjs +154 -0
- package/lib/tagfeed.mjs +158 -0
- package/lib/ua.mjs +29 -0
- package/lib/update.mjs +91 -0
- package/lib/webpush.mjs +98 -0
- package/lib/wire.mjs +762 -0
- package/package.json +60 -0
- package/phanpy/dist/404.html +40 -0
- package/phanpy/dist/_headers +2 -0
- package/phanpy/dist/apple-touch-icon.png +0 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js +2 -0
- package/phanpy/dist/assets/ICONS-DIPJeU0C.js.map +1 -0
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js +183 -0
- package/phanpy/dist/assets/ScheduledAtField-D-Abfo4J.js.map +1 -0
- package/phanpy/dist/assets/Temml-CCjoacWt.woff2 +0 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js +2 -0
- package/phanpy/dist/assets/__vite-browser-external-C7iut881.js.map +1 -0
- package/phanpy/dist/assets/boosts-carousel-BiOaNdMT.jpg +0 -0
- package/phanpy/dist/assets/catch-up-O5q5C75Z.png +0 -0
- package/phanpy/dist/assets/chunk-aKtaBQYM.js +1 -0
- package/phanpy/dist/assets/compose-CFEx7wb4.js +2 -0
- package/phanpy/dist/assets/compose-CFEx7wb4.js.map +1 -0
- package/phanpy/dist/assets/compose-ZJN5xSb4.js +35 -0
- package/phanpy/dist/assets/compose-ZJN5xSb4.js.map +1 -0
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js +52 -0
- package/phanpy/dist/assets/debug.module-YFQgwnIr.js.map +1 -0
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js +2 -0
- package/phanpy/dist/assets/dom-p1YcJ5Rw.js.map +1 -0
- package/phanpy/dist/assets/home-mobile-dark@2x-Dc-J8-SM.png +0 -0
- package/phanpy/dist/assets/home-mobile-light@2x-Bd3fod8m.png +0 -0
- package/phanpy/dist/assets/home-tablet-dark@2x-BgcNI8le.png +0 -0
- package/phanpy/dist/assets/home-tablet-light@2x-BW2RV9T6.png +0 -0
- package/phanpy/dist/assets/icons/add-circle-line-WD0mwORD.js +1 -0
- package/phanpy/dist/assets/icons/alert-line-CpFUMf9s.js +1 -0
- package/phanpy/dist/assets/icons/android-2-line-Bzxnrd3S.js +1 -0
- package/phanpy/dist/assets/icons/announcement-line-5nwaJFJm.js +1 -0
- package/phanpy/dist/assets/icons/arrow-down-circle-line-a7Id9-Iv.js +1 -0
- package/phanpy/dist/assets/icons/arrow-down-line-B8uzXzq6.js +1 -0
- package/phanpy/dist/assets/icons/arrow-left-line-CGrGT9C-.js +1 -0
- package/phanpy/dist/assets/icons/arrow-right-line-CE-HERfX.js +1 -0
- package/phanpy/dist/assets/icons/arrow-to-up-line-CvdQjmcy.js +1 -0
- package/phanpy/dist/assets/icons/arrow-up-circle-line-DEparSNF.js +1 -0
- package/phanpy/dist/assets/icons/arrow-up-line-C7yBeugc.js +1 -0
- package/phanpy/dist/assets/icons/arrows-right-line-BtbXmJGP.js +1 -0
- package/phanpy/dist/assets/icons/at-line-CF66Bznk.js +1 -0
- package/phanpy/dist/assets/icons/attachment-line-BY8It-l9.js +1 -0
- package/phanpy/dist/assets/icons/blockquote-line-BS6pU_hm.js +1 -0
- package/phanpy/dist/assets/icons/board-line-C0UWXyA6.js +1 -0
- package/phanpy/dist/assets/icons/bookmark-line-1CtX5JLF.js +1 -0
- package/phanpy/dist/assets/icons/building-5-line-Dd59MzbO.js +1 -0
- package/phanpy/dist/assets/icons/bus-2-line-6_dvA0GG.js +1 -0
- package/phanpy/dist/assets/icons/calendar-day-line-Crpl_fmu.js +1 -0
- package/phanpy/dist/assets/icons/calendar-month-line-vommts2C.js +1 -0
- package/phanpy/dist/assets/icons/calendar-time-add-line-BU_DTlQk.js +1 -0
- package/phanpy/dist/assets/icons/camera-line-Cv69MEei.js +1 -0
- package/phanpy/dist/assets/icons/celebrate-line-B8DvIoi0.js +1 -0
- package/phanpy/dist/assets/icons/chart-bar-line-FK-QOmtF.js +1 -0
- package/phanpy/dist/assets/icons/chart-line-line-BGcJAFWw.js +1 -0
- package/phanpy/dist/assets/icons/chat-3-line-YFK2-hwz.js +1 -0
- package/phanpy/dist/assets/icons/check-circle-line-DsAxFr5-.js +1 -0
- package/phanpy/dist/assets/icons/clipboard-line-BBp364gJ.js +1 -0
- package/phanpy/dist/assets/icons/close-circle-line-CICdiuGz.js +1 -0
- package/phanpy/dist/assets/icons/close-line-DYrFWcjv.js +1 -0
- package/phanpy/dist/assets/icons/cloud-line-tiQAbah8.js +1 -0
- package/phanpy/dist/assets/icons/code-line-fAl8iiV3.js +1 -0
- package/phanpy/dist/assets/icons/comment-2-line-CR3g7WeB.js +1 -0
- package/phanpy/dist/assets/icons/copy-2-line-CLW69yF5.js +1 -0
- package/phanpy/dist/assets/icons/delete-2-line-B8xmbr4n.js +1 -0
- package/phanpy/dist/assets/icons/document-3-line-DBGNhAg2.js +1 -0
- package/phanpy/dist/assets/icons/document-line-C7mb9sb7.js +1 -0
- package/phanpy/dist/assets/icons/down-line-BGqSSThd.js +1 -0
- package/phanpy/dist/assets/icons/edit-4-line-DcgGaEhx.js +1 -0
- package/phanpy/dist/assets/icons/emoji-2-line-D8YgnM-y.js +1 -0
- package/phanpy/dist/assets/icons/exit-line-Dc07BS-x.js +1 -0
- package/phanpy/dist/assets/icons/external-link-line-BvpSkmjO.js +1 -0
- package/phanpy/dist/assets/icons/eye-2-line-CiWXx-f_.js +1 -0
- package/phanpy/dist/assets/icons/eye-close-line-BgTaSJ4P.js +1 -0
- package/phanpy/dist/assets/icons/filter-2-line-B_mR2usy.js +1 -0
- package/phanpy/dist/assets/icons/filter-line-C_nm-3cU.js +1 -0
- package/phanpy/dist/assets/icons/flag-1-line-C2HB05I0.js +1 -0
- package/phanpy/dist/assets/icons/forbid-circle-line-D3LTT9yd.js +1 -0
- package/phanpy/dist/assets/icons/formula-line-DLrt2d6j.js +1 -0
- package/phanpy/dist/assets/icons/grid-line-CgUmEikf.js +1 -0
- package/phanpy/dist/assets/icons/group-line-C5fnX0wU.js +1 -0
- package/phanpy/dist/assets/icons/hand-finger-2-line-BGgxbuL9.js +1 -0
- package/phanpy/dist/assets/icons/hashtag-line-CyTEEXBb.js +1 -0
- package/phanpy/dist/assets/icons/heart-crack-line-BZddbO2C.js +1 -0
- package/phanpy/dist/assets/icons/heart-line-CJ7K47J4.js +1 -0
- package/phanpy/dist/assets/icons/history-2-line-kWWvFzqv.js +1 -0
- package/phanpy/dist/assets/icons/history-line-bGQiwbTH.js +1 -0
- package/phanpy/dist/assets/icons/home-3-line-fRU1zeLG.js +1 -0
- package/phanpy/dist/assets/icons/information-line-iNk6-sOY.js +1 -0
- package/phanpy/dist/assets/icons/keyboard-line-DsCyuQNz.js +1 -0
- package/phanpy/dist/assets/icons/layout-4-line-CZkOX52Z.js +1 -0
- package/phanpy/dist/assets/icons/layout-5-line-B_znt5wO.js +1 -0
- package/phanpy/dist/assets/icons/left-line-eUw0tya4.js +1 -0
- package/phanpy/dist/assets/icons/lightning-line-DaKoDdix.js +1 -0
- package/phanpy/dist/assets/icons/link-2-line-DCqLmMA7.js +1 -0
- package/phanpy/dist/assets/icons/list-check-line-BAxosBh9.js +1 -0
- package/phanpy/dist/assets/icons/lock-line-CPoaXeUW.js +1 -0
- package/phanpy/dist/assets/icons/mail-line-B0P8aa1L.js +1 -0
- package/phanpy/dist/assets/icons/moon-line-CEqNAToP.js +1 -0
- package/phanpy/dist/assets/icons/more-1-fill-Dw2e5xab.js +1 -0
- package/phanpy/dist/assets/icons/more-3-line-BIiVjYTz.js +1 -0
- package/phanpy/dist/assets/icons/notification-line-BZZq2Gtu.js +1 -0
- package/phanpy/dist/assets/icons/pencil-line-CKh8-A1v.js +1 -0
- package/phanpy/dist/assets/icons/photo-album-line-Bnfg2u40.js +1 -0
- package/phanpy/dist/assets/icons/pin-line-DpTolyhs.js +1 -0
- package/phanpy/dist/assets/icons/play-fill-B0-G_8lX.js +1 -0
- package/phanpy/dist/assets/icons/qrcode-2-line-B6SPCaHn.js +1 -0
- package/phanpy/dist/assets/icons/quill-pen-line-B6LUrnCP.js +1 -0
- package/phanpy/dist/assets/icons/quote-left-fill-hQZ1cj_6.js +1 -0
- package/phanpy/dist/assets/icons/radar-line-CKyroTth.js +1 -0
- package/phanpy/dist/assets/icons/react-line-CoIKhhtK.js +1 -0
- package/phanpy/dist/assets/icons/refresh-2-line-D0mTF97S.js +1 -0
- package/phanpy/dist/assets/icons/right-line-B3KWJaqc.js +1 -0
- package/phanpy/dist/assets/icons/rocket-line-4KZl537D.js +1 -0
- package/phanpy/dist/assets/icons/round-fill-C69NTrZ0.js +1 -0
- package/phanpy/dist/assets/icons/round-line-B9k_1uo-.js +1 -0
- package/phanpy/dist/assets/icons/route-line-B28ReHcE.js +1 -0
- package/phanpy/dist/assets/icons/rows-4-line-CrBniagM.js +1 -0
- package/phanpy/dist/assets/icons/scan-line-DSf74YdP.js +1 -0
- package/phanpy/dist/assets/icons/search-2-line-B6geIU1z.js +1 -0
- package/phanpy/dist/assets/icons/settings-3-line-azJwK47w.js +1 -0
- package/phanpy/dist/assets/icons/settings-6-line-C_5zc5YR.js +1 -0
- package/phanpy/dist/assets/icons/share-2-line-D5MxWjrm.js +1 -0
- package/phanpy/dist/assets/icons/share-forward-line-Cp61fZzZ.js +1 -0
- package/phanpy/dist/assets/icons/sparkles-2-line-DYYyXTaW.js +1 -0
- package/phanpy/dist/assets/icons/sparkles-line-C2hY1s6I.js +1 -0
- package/phanpy/dist/assets/icons/time-line-BF3eet1v.js +1 -0
- package/phanpy/dist/assets/icons/transfer-4-line-oisfvTBw.js +1 -0
- package/phanpy/dist/assets/icons/translate-line-BFWol2Ce.js +1 -0
- package/phanpy/dist/assets/icons/unlock-line-X_x5vwv3.js +1 -0
- package/phanpy/dist/assets/icons/upload-3-line-DgzwUJeW.js +1 -0
- package/phanpy/dist/assets/icons/user-4-line-BVSynZCp.js +1 -0
- package/phanpy/dist/assets/icons/user-add-2-line-BlGS5Q8q.js +1 -0
- package/phanpy/dist/assets/icons/user-add-line-BbXdHG4z.js +1 -0
- package/phanpy/dist/assets/icons/user-edit-line-BFAHOV0X.js +1 -0
- package/phanpy/dist/assets/icons/user-follow-line-BXux0wSd.js +1 -0
- package/phanpy/dist/assets/icons/user-star-line-DdYlpHLU.js +1 -0
- package/phanpy/dist/assets/icons/user-warning-line-Cj43j0_E.js +1 -0
- package/phanpy/dist/assets/icons/user-x-line-DIDgNlPH.js +1 -0
- package/phanpy/dist/assets/icons/volume-line-Bs390KQt.js +1 -0
- package/phanpy/dist/assets/icons/volume-mute-line-DzlzIqFZ.js +1 -0
- package/phanpy/dist/assets/icons/walk-line-yNXfB-md.js +1 -0
- package/phanpy/dist/assets/icons/world-2-line-CenkGIwj.js +1 -0
- package/phanpy/dist/assets/icons/zoom-in-line-CaENUphf.js +1 -0
- package/phanpy/dist/assets/icons/zoom-out-line-BDNk0EWo.js +1 -0
- package/phanpy/dist/assets/instances-BNPptnn-.json +333 -0
- package/phanpy/dist/assets/locales/ar-SA-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/ca-ES-DHRW86Tz.js +1 -0
- package/phanpy/dist/assets/locales/cs-CZ-CKoqEuA1.js +1 -0
- package/phanpy/dist/assets/locales/de-DE-ByRPMRFy.js +1 -0
- package/phanpy/dist/assets/locales/eo-UY-CPC7wLCS.js +1 -0
- package/phanpy/dist/assets/locales/es-ES-dJa0Fd9t.js +1 -0
- package/phanpy/dist/assets/locales/eu-ES-C3djlPR2.js +1 -0
- package/phanpy/dist/assets/locales/fa-IR-DTT490Ck.js +1 -0
- package/phanpy/dist/assets/locales/fi-FI-B23MCPuz.js +1 -0
- package/phanpy/dist/assets/locales/fr-FR-BcaQsO5C.js +1 -0
- package/phanpy/dist/assets/locales/gl-ES-BR-CVKBN.js +1 -0
- package/phanpy/dist/assets/locales/he-IL-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/hu-HU-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/it-IT-C1136UFV.js +1 -0
- package/phanpy/dist/assets/locales/ja-JP-CRZmRAhD.js +1 -0
- package/phanpy/dist/assets/locales/kab-Cz8xH_TT.js +1 -0
- package/phanpy/dist/assets/locales/ko-KR-CG9UiY4o.js +1 -0
- package/phanpy/dist/assets/locales/lt-LT-CJcI6sHZ.js +1 -0
- package/phanpy/dist/assets/locales/nb-NO-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/nl-NL-DxN4Yc1t.js +1 -0
- package/phanpy/dist/assets/locales/oc-FR-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/pl-PL-DkwGXf6P.js +1 -0
- package/phanpy/dist/assets/locales/pseudo-LOCALE-CQ4XxkQ0.js +1 -0
- package/phanpy/dist/assets/locales/pt-BR-nGL0HdFS.js +1 -0
- package/phanpy/dist/assets/locales/pt-PT-Cp17aj2z.js +1 -0
- package/phanpy/dist/assets/locales/ru-RU-0Y8cIoaK.js +1 -0
- package/phanpy/dist/assets/locales/th-TH-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/tok-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/tr-TR-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/locales/uk-UA-C5E3dFBf.js +1 -0
- package/phanpy/dist/assets/locales/zh-CN-BHJi7_et.js +1 -0
- package/phanpy/dist/assets/locales/zh-TW-CfheRbCu.js +1 -0
- package/phanpy/dist/assets/main-BdqNbG-a.js +26 -0
- package/phanpy/dist/assets/main-BdqNbG-a.js.map +1 -0
- package/phanpy/dist/assets/mock-home-DGhR__He.js +2 -0
- package/phanpy/dist/assets/mock-home-DGhR__He.js.map +1 -0
- package/phanpy/dist/assets/multi-column-ROck0NVt.jpg +0 -0
- package/phanpy/dist/assets/multi-hashtag-timeline-DH5INVHi.jpg +0 -0
- package/phanpy/dist/assets/nested-comments-thread-hv59kZLc.jpg +0 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js +2 -0
- package/phanpy/dist/assets/polyfill-force-H8sUkLGp.js.map +1 -0
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js +20 -0
- package/phanpy/dist/assets/pwa-viewport-ogKbF51K.js.map +1 -0
- package/phanpy/dist/assets/style-B1iLPTPR.css +2 -0
- package/phanpy/dist/assets/temml-DuyIQMei.js +97 -0
- package/phanpy/dist/assets/temml-DuyIQMei.js.map +1 -0
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js +2 -0
- package/phanpy/dist/assets/tinyld.light.node-DtbBhLYC.js.map +1 -0
- package/phanpy/dist/assets/year-in-posts-B2lywqdx.png +0 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js +3 -0
- package/phanpy/dist/assets/year-in-posts-DJv_star.js.map +1 -0
- package/phanpy/dist/compose/index.html +20 -0
- package/phanpy/dist/favicon.ico +0 -0
- package/phanpy/dist/index.html +73 -0
- package/phanpy/dist/logo-192.png +0 -0
- package/phanpy/dist/logo-512.png +0 -0
- package/phanpy/dist/logo-badge-72.png +0 -0
- package/phanpy/dist/logo-maskable-512.png +0 -0
- package/phanpy/dist/logo-monochrome-512.png +0 -0
- package/phanpy/dist/logo-monochrome-maskable-512.png +0 -0
- package/phanpy/dist/manifest.webmanifest +1 -0
- package/phanpy/dist/og-image-2.jpg +0 -0
- package/phanpy/dist/og-image.png +0 -0
- package/phanpy/dist/robots.txt +2 -0
- package/phanpy/dist/sw.js +25 -0
- package/phanpy/dist/version.json +1 -0
- package/run-agent.mjs +805 -0
- package/scripts/build-dist.mjs +37 -0
- package/scripts/verify-ui.mjs +60 -0
- package/ui/README.md +23 -0
- package/vendor/gate.cjs +132 -0
- package/vendor/idp-grant.cjs +394 -0
- package/vendor/jfetch.cjs +16 -0
- package/web/admin/admin.js +958 -0
- package/web/admin/bar.css +64 -0
- package/web/admin/bar.js +61 -0
- package/web/admin/client/client.js +66 -0
- package/web/admin/client/index.html +42 -0
- package/web/admin/index.html +493 -0
- package/web/admin/setup/index.html +172 -0
- package/web/admin/setup/setup.js +268 -0
- package/web/admin/window.css +55 -0
- package/web/admin/window.js +136 -0
- package/web/front/install.sh +69 -0
- package/web/front/new-account.html +424 -0
- package/web/front/run.html +138 -0
- package/web/front/solid-client-authn.bundle.js +2 -0
|
@@ -0,0 +1,493 @@
|
|
|
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>FediPod — record</title>
|
|
7
|
+
<style>
|
|
8
|
+
/* 20px on the root, not on body, so every rem below scales with it.
|
|
9
|
+
Two accents, kept apart on purpose: --link is anything you can act on,
|
|
10
|
+
--heading is structure. Using one colour for both made the headings
|
|
11
|
+
look clickable. */
|
|
12
|
+
/* One control chassis, one selector per element kind so nothing can fall
|
|
13
|
+
through unstyled: every control sits in the same light well; a select
|
|
14
|
+
carries a drawn chevron, a button carries the action-blue text. */
|
|
15
|
+
:root { color-scheme: light dark; font-size: 112.5%;
|
|
16
|
+
--bar-col: 1280px; /* the bar centres on the same column */
|
|
17
|
+
--link: #1a4f8a; --heading: #5b3a8e;
|
|
18
|
+
--ink: #1b1c1e; --muted: #5f6368;
|
|
19
|
+
--btn-bg: #f4efe6; --btn-bg-hover: #ece3d2; --btn-bg-active: #e2d5bd; --btn-edge: #8a7a61;
|
|
20
|
+
--field-bg: #ffffff; --field-bg-hover: #f7f4ee; --field-edge: #767676;
|
|
21
|
+
--danger: #b00020; --ring: var(--link);
|
|
22
|
+
--chevron: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23444444' stroke-width='1.6' stroke-linecap='round'/></svg>"); }
|
|
23
|
+
@media (prefers-color-scheme: dark) {
|
|
24
|
+
:root { --link: #7fb3e8; --heading: #b9a3e3;
|
|
25
|
+
--ink: #e8eaed; --muted: #a5a9ad;
|
|
26
|
+
--btn-bg: #35302a; --btn-bg-hover: #423b31; --btn-bg-active: #4d4436; --btn-edge: #9a8d7a;
|
|
27
|
+
--field-bg: #383d44; --field-bg-hover: #40464e; --field-edge: #9aa0a6;
|
|
28
|
+
--danger: #ff8a8a;
|
|
29
|
+
--chevron: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23dddddd' stroke-width='1.6' stroke-linecap='round'/></svg>"); }
|
|
30
|
+
}
|
|
31
|
+
/* The page itself never scrolls: the body is exactly the window; the content
|
|
32
|
+
column beside the rail is the scroll container. */
|
|
33
|
+
html { height: 100%; }
|
|
34
|
+
body { font: 1rem/1.5 system-ui, sans-serif; margin: 0; height: 100dvh;
|
|
35
|
+
box-sizing: border-box; display: flex; flex-direction: column; overflow: hidden; }
|
|
36
|
+
/* The bar spans the window; the rail and the content column share one centred
|
|
37
|
+
column underneath it — wide enough that the content keeps 960 of its own
|
|
38
|
+
beside the expanded rail. */
|
|
39
|
+
#page { flex: 1 1 auto; min-height: 0; width: 100%; max-width: 1280px; margin: 0 auto;
|
|
40
|
+
padding: 1.25rem 1rem 1rem; box-sizing: border-box;
|
|
41
|
+
display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 1.25rem; }
|
|
42
|
+
#content { min-width: 0; min-height: 0; overflow-y: auto; }
|
|
43
|
+
/* Two cases where the page may grow past the window: the new-account form, and
|
|
44
|
+
a group console, whose lists have no bound. Both scroll on the window. */
|
|
45
|
+
body.adding, body.group { height: auto; min-height: 100dvh; overflow: auto; }
|
|
46
|
+
body.adding #page { display: block; }
|
|
47
|
+
body.adding #content, body.group #content { overflow: visible; min-height: auto; }
|
|
48
|
+
body.group #page { min-height: auto; }
|
|
49
|
+
body.adding #rail { display: none; }
|
|
50
|
+
/* The action rail: a raised card of everything you can DO, so the facts keep
|
|
51
|
+
the page. Collapsed it is a slim strip holding only the toggle arrow. */
|
|
52
|
+
#rail { width: 14.5rem; box-sizing: border-box; padding: .6rem .8rem .9rem;
|
|
53
|
+
align-self: start; max-height: 100%; overflow-y: auto;
|
|
54
|
+
background: var(--raised); border-radius: .5rem; }
|
|
55
|
+
#rail.closed { width: auto; padding: .4rem; }
|
|
56
|
+
#rail.closed .rail-groups { display: none; }
|
|
57
|
+
#rail-toggle { display: flex; align-items: center; justify-content: flex-end;
|
|
58
|
+
width: 100%; margin: 0; padding: .2rem .3rem; border: 0;
|
|
59
|
+
background: none; font: inherit; font-weight: 600; color: var(--heading);
|
|
60
|
+
cursor: pointer; border-radius: .3rem; }
|
|
61
|
+
#rail-toggle:hover, #rail-toggle:active { background: none; }
|
|
62
|
+
.rail-arrow { color: var(--link); }
|
|
63
|
+
.rail-group h2 { display: block; font-size: .9rem; font-weight: 600;
|
|
64
|
+
text-transform: uppercase; letter-spacing: .06em; margin: .9rem 0 .35rem; }
|
|
65
|
+
.rail-group button { display: block; width: 100%; text-align: left;
|
|
66
|
+
margin: .35rem 0; font-size: .95rem; padding: .35rem .7rem; font-weight: 400; }
|
|
67
|
+
.rail-group .apart { margin-top: 1rem; }
|
|
68
|
+
/* One column on a phone: the rail becomes a strip of pills above the facts. */
|
|
69
|
+
@media (max-width: 47rem) {
|
|
70
|
+
#page { display: flex; flex-direction: column; gap: .9rem; }
|
|
71
|
+
#rail { width: auto; max-height: none; overflow: visible; }
|
|
72
|
+
.rail-group { display: flex; flex-wrap: wrap; align-items: baseline; gap: .1rem .45rem; }
|
|
73
|
+
.rail-group h2 { flex: 0 0 100%; margin: .5rem 0 .1rem; }
|
|
74
|
+
.rail-group button { display: inline-block; width: auto; margin: .2rem 0; }
|
|
75
|
+
#content { flex: 1 1 auto; }
|
|
76
|
+
}
|
|
77
|
+
/* `hidden` is a UA rule, so ANY author `display` beats it — `dl { display: grid }`
|
|
78
|
+
left the facts on screen with the attribute set. Everything here toggles with
|
|
79
|
+
`hidden`, so it has to outrank them all. */
|
|
80
|
+
[hidden] { display: none !important; }
|
|
81
|
+
/* The two status regions are never `hidden`: a live region put into the
|
|
82
|
+
accessibility tree in the same task as its text does not reliably announce,
|
|
83
|
+
so they stay in it and disappear by being empty instead. */
|
|
84
|
+
#say:empty, #fatal:empty { display: none; }
|
|
85
|
+
h1, h2, h3 { color: var(--heading); }
|
|
86
|
+
a { color: var(--link); }
|
|
87
|
+
h2, h3 { font-size: 1.15rem; font-weight: 600; display: flex; flex-wrap: wrap;
|
|
88
|
+
align-items: baseline; gap: .6rem; }
|
|
89
|
+
h2 { margin: 1.5rem 0 .5rem; }
|
|
90
|
+
h3 { margin: 1.25rem 0 .35rem; }
|
|
91
|
+
#facts { margin: .75rem 0 0; }
|
|
92
|
+
/* They sit after the kind, on that row, so they need a gap from the word and
|
|
93
|
+
must not stretch the row's line height. */
|
|
94
|
+
#moderation { margin-left: .6rem; word-break: normal; }
|
|
95
|
+
#moderation label { display: inline; margin: 0 .3rem 0 .5rem; font-weight: normal; color: var(--muted); }
|
|
96
|
+
p { margin: .5rem 0; }
|
|
97
|
+
.sub { color: var(--muted); margin-top: 0; }
|
|
98
|
+
fieldset { border: 1px solid #bbb; border-radius: .4rem; margin: 1rem 0; padding: .75rem 1rem 1rem; }
|
|
99
|
+
legend { padding: 0 .3rem; font-weight: 600; color: var(--heading); }
|
|
100
|
+
label { display: block; margin: .75rem 0 .2rem; }
|
|
101
|
+
.hint { color: var(--muted); font-size: .9rem; margin: .15rem 0 0; }
|
|
102
|
+
/* SETTINGS: light wells, unmistakably fields, in both schemes. */
|
|
103
|
+
input[type=text], input[type=email], input[type=url], input[type=password], textarea {
|
|
104
|
+
font: inherit; width: 100%; padding: .5rem; box-sizing: border-box;
|
|
105
|
+
border: 1px solid var(--field-edge); border-radius: .3rem;
|
|
106
|
+
background: var(--field-bg); color: var(--ink); }
|
|
107
|
+
input::placeholder { color: var(--muted); opacity: 1; }
|
|
108
|
+
select { font: inherit; appearance: none; max-width: 100%; min-width: 0;
|
|
109
|
+
padding: .15rem 1.7rem .15rem .55rem;
|
|
110
|
+
border: 1px solid var(--field-edge); border-radius: .3rem;
|
|
111
|
+
color: var(--ink); cursor: pointer;
|
|
112
|
+
background-color: var(--field-bg); background-image: var(--chevron);
|
|
113
|
+
background-repeat: no-repeat; background-position: right .55rem center;
|
|
114
|
+
background-size: .6rem auto; }
|
|
115
|
+
select:hover { background-color: var(--field-bg-hover); }
|
|
116
|
+
/* ACTIONS: identical to the settings — same well, same border, same ink,
|
|
117
|
+
Retire included. One control look for the whole page. */
|
|
118
|
+
button { font: inherit; padding: .45rem 1rem; border-radius: .3rem; border: 1px solid var(--field-edge);
|
|
119
|
+
background: var(--field-bg); color: var(--ink); cursor: pointer; }
|
|
120
|
+
button:hover { background: var(--field-bg-hover); }
|
|
121
|
+
button:active { background: var(--field-bg-hover); }
|
|
122
|
+
button.primary { font-weight: 600; }
|
|
123
|
+
button:disabled { opacity: .55; cursor: default; }
|
|
124
|
+
/* Small buttons that sit in a line of text. */
|
|
125
|
+
button.inline { font-size: .9rem; padding: .15rem .6rem; font-weight: 400; }
|
|
126
|
+
/* One visible ring for everything focusable. */
|
|
127
|
+
:is(button, select, input, textarea, a):focus-visible {
|
|
128
|
+
outline: 2px solid var(--ring); outline-offset: 2px; }
|
|
129
|
+
dl { margin: .5rem 0; } /* grid only above 34rem — see the media query below */
|
|
130
|
+
@media (min-width: 34rem) {
|
|
131
|
+
dl { display: grid; grid-template-columns: max-content minmax(0, 1fr);
|
|
132
|
+
gap: .6rem .9rem; align-items: baseline; }
|
|
133
|
+
}
|
|
134
|
+
dt { color: var(--muted); }
|
|
135
|
+
dd { margin: 0; min-width: 0; overflow-wrap: anywhere; }
|
|
136
|
+
.rows { list-style: none; padding: 0; margin: .3rem 0; }
|
|
137
|
+
.rows li { display: flex; flex-wrap: wrap; gap: .5rem; align-items: center;
|
|
138
|
+
padding: .4rem 0; border-top: 1px solid #ddd; }
|
|
139
|
+
.rows li span { flex: 1 1 18rem; overflow-wrap: anywhere; }
|
|
140
|
+
.muted { color: var(--muted); }
|
|
141
|
+
/* The actors dropdown rides the bar, at the bar's own text scale. */
|
|
142
|
+
#bar select { font-size: .95rem; }
|
|
143
|
+
#bsky-ctl { display: inline-flex; gap: .6rem; align-items: baseline; margin-left: .6rem; }
|
|
144
|
+
/* The alias row inside the Transfer-an-account-here panel. */
|
|
145
|
+
#alias-ctl { display: flex; gap: .5rem; align-items: center; margin-top: .3rem; }
|
|
146
|
+
#alias-ctl input { flex: 1 1 12rem; width: auto; }
|
|
147
|
+
#alias-chips span { display: inline-flex; align-items: center; gap: .35rem;
|
|
148
|
+
border: 1px solid var(--btn-edge); border-radius: 999px;
|
|
149
|
+
padding: .05rem .25rem .05rem .75rem; margin: 0 .3rem .3rem 0; }
|
|
150
|
+
/* Radios read as one line each: label { display: block } alone drops the text
|
|
151
|
+
below the control. */
|
|
152
|
+
.choice { display: block; margin: .35rem 0; font-weight: normal; }
|
|
153
|
+
.choice input { width: auto; margin-right: .4rem; }
|
|
154
|
+
.warn { border-left: .25rem solid #c60; padding-left: .75rem; }
|
|
155
|
+
.err { border-left: .25rem solid var(--danger); padding-left: .75rem; color: var(--danger); }
|
|
156
|
+
.ok { border-left: .25rem solid #2a7; padding-left: .75rem; }
|
|
157
|
+
pre { overflow-x: auto; background: #0001; padding: .6rem; border-radius: .3rem; font-size: .9rem; }
|
|
158
|
+
@media (prefers-color-scheme: dark) {
|
|
159
|
+
fieldset { border-color: #555; } .rows li { border-color: #444; }
|
|
160
|
+
}
|
|
161
|
+
</style>
|
|
162
|
+
<link rel="stylesheet" href="bar.css">
|
|
163
|
+
<link rel="stylesheet" href="window.css">
|
|
164
|
+
</head>
|
|
165
|
+
<body>
|
|
166
|
+
|
|
167
|
+
<!-- The same bar the client view carries: one thing, two views. -->
|
|
168
|
+
<nav id="bar" aria-label="Site">
|
|
169
|
+
<h1 class="name">FediPod</h1>
|
|
170
|
+
<select id="actor-pick" aria-label="Local actors"
|
|
171
|
+
title=" Every actor on this machine; choosing one goes to its record"></select>
|
|
172
|
+
<a id="bar-fediverse" href="client/" title=" Open the client and read your timeline">visit account</a>
|
|
173
|
+
<a id="bar-manage" href="./" title=" The record: what this actor is, and what you can do to it" aria-current="page">manage account</a>
|
|
174
|
+
</nav>
|
|
175
|
+
<main id="page">
|
|
176
|
+
|
|
177
|
+
<!-- Everything you can DO, in one collapsible card; the facts keep the page.
|
|
178
|
+
The buttons are the same ones the two action rows used to hold — same
|
|
179
|
+
ids, same floating panels. -->
|
|
180
|
+
<nav id="rail" hidden aria-label="Actions">
|
|
181
|
+
<button type="button" id="rail-toggle" aria-expanded="true" aria-label="Actions"
|
|
182
|
+
title=" Hide the action panel"><span class="rail-arrow" aria-hidden="true">«</span></button>
|
|
183
|
+
<div class="rail-groups">
|
|
184
|
+
<section class="rail-group" aria-label="Upkeep">
|
|
185
|
+
<h2>Upkeep</h2>
|
|
186
|
+
<button id="do-drain" title=" Fetch and handle whatever is waiting in the inbox now">Drain the inbox</button>
|
|
187
|
+
<button id="do-rebuild" title=" Put back posts this machine lost, from what the pod still holds">Recover posts</button>
|
|
188
|
+
<button id="do-log" title=" This agent's recent log lines">Show the log</button>
|
|
189
|
+
<button id="do-deadletter" title=" Deliveries that failed for good and were set aside">Show dead letters</button>
|
|
190
|
+
</section>
|
|
191
|
+
<section class="rail-group" aria-label="Lifecycle">
|
|
192
|
+
<h2>Lifecycle</h2>
|
|
193
|
+
<button id="do-rotate-key" title=" Replace the signing key and tell other servers the new one" data-confirm="rotate-key">Rotate signing key</button>
|
|
194
|
+
<button id="do-transfer-in" title=" List an old account as an alias, so its server sends its followers here">Transfer an account here</button>
|
|
195
|
+
<button id="do-move" title=" Your followers go to the account you name; this handle keeps resolving as a redirect" data-confirm="move">Transfer this account away</button>
|
|
196
|
+
<button id="do-retire" title=" End this identity for good — a Delete, then a Tombstone" class="danger apart" data-confirm="retire">Retire identity</button>
|
|
197
|
+
</section>
|
|
198
|
+
</div>
|
|
199
|
+
</nav>
|
|
200
|
+
|
|
201
|
+
<div id="content">
|
|
202
|
+
<p class="err" id="fatal" role="alert"></p>
|
|
203
|
+
<p aria-live="polite" id="say"></p>
|
|
204
|
+
|
|
205
|
+
<!-- Every actor running on this machine. A stopped one is left out: there is
|
|
206
|
+
no page there to open. Plain links — a top-level navigation sends no
|
|
207
|
+
Origin, so each agent's own firewall lets it through. -->
|
|
208
|
+
<section id="pane-others" aria-label="Actors on this machine" hidden>
|
|
209
|
+
<!-- Everything setup cannot work without. The bio, avatar and UI password it
|
|
210
|
+
also offers are all changeable afterwards, so they are not asked here —
|
|
211
|
+
the fuller form is what the new actor opens on. -->
|
|
212
|
+
<form id="new-actor-form" hidden>
|
|
213
|
+
<fieldset>
|
|
214
|
+
<legend>What kind of Actor?</legend>
|
|
215
|
+
<label class="choice"><input type="radio" name="newKind" value="person" checked>
|
|
216
|
+
A <strong>person</strong> — an account you read and post from</label>
|
|
217
|
+
<label class="choice"><input type="radio" name="newKind" value="group">
|
|
218
|
+
A <strong>group</strong> of fediverse users you manage</label>
|
|
219
|
+
</fieldset>
|
|
220
|
+
|
|
221
|
+
<fieldset>
|
|
222
|
+
<legend>Actor's Handle</legend>
|
|
223
|
+
<label for="new-handle">Handle</label>
|
|
224
|
+
<input type="text" id="new-handle" autocomplete="off"
|
|
225
|
+
placeholder="handle part of fediverse address: @HANDLE@server"
|
|
226
|
+
aria-describedby="new-handle-hint">
|
|
227
|
+
<p class="hint" id="new-handle-hint">The name in your fediverse address. This is permanent.
|
|
228
|
+
Letters, digits, hyphens, and underscores only.</p>
|
|
229
|
+
</fieldset>
|
|
230
|
+
|
|
231
|
+
<fieldset>
|
|
232
|
+
<legend>Actor's Pod</legend>
|
|
233
|
+
<label class="choice"><input type="radio" name="newMode" value="new" checked>
|
|
234
|
+
Create a Solid account and pod for the Actor</label>
|
|
235
|
+
<label class="choice"><input type="radio" name="newMode" value="existing">
|
|
236
|
+
Use a pod I already have</label>
|
|
237
|
+
|
|
238
|
+
<label for="new-issuer">Solid identity provider</label>
|
|
239
|
+
<input type="url" id="new-issuer" value="https://solidcommunity.net" autocomplete="url">
|
|
240
|
+
|
|
241
|
+
<label for="new-email">Solid Account email/username</label>
|
|
242
|
+
<input type="email" id="new-email" autocomplete="email" placeholder="you@example.org">
|
|
243
|
+
|
|
244
|
+
<label for="new-password">Solid Account password</label>
|
|
245
|
+
<input type="password" id="new-password" autocomplete="current-password"
|
|
246
|
+
aria-describedby="new-password-hint">
|
|
247
|
+
<p class="hint" id="new-password-hint">Password is sent once in return for a credential which is stored locally.
|
|
248
|
+
The password itself is never stored. You may revoke the credential without changing the
|
|
249
|
+
password.</p>
|
|
250
|
+
|
|
251
|
+
<div id="new-row-podname">
|
|
252
|
+
<label for="new-podname">Pod name</label>
|
|
253
|
+
<input type="text" id="new-podname" autocomplete="off" aria-describedby="new-podname-hint">
|
|
254
|
+
<p class="hint" id="new-podname-hint">The subdomain of your pod address, it will become
|
|
255
|
+
https://YOUR-POD-NAME.your-identity-provider</p>
|
|
256
|
+
</div>
|
|
257
|
+
<div id="new-row-pod" hidden>
|
|
258
|
+
<label for="new-pod">Pod address</label>
|
|
259
|
+
<input type="url" id="new-pod" autocomplete="url" placeholder="https://you.solidcommunity.net/">
|
|
260
|
+
</div>
|
|
261
|
+
</fieldset>
|
|
262
|
+
|
|
263
|
+
<p>
|
|
264
|
+
<button type="submit" id="new-actor-go" title=" Create the account, the pod and the actor" class="primary">Create</button>
|
|
265
|
+
<button type="button" id="new-actor-cancel" title=" Change nothing and close">Cancel</button>
|
|
266
|
+
</p>
|
|
267
|
+
</form>
|
|
268
|
+
|
|
269
|
+
</section>
|
|
270
|
+
|
|
271
|
+
<section id="pane-identity" hidden>
|
|
272
|
+
<dl id="facts"></dl>
|
|
273
|
+
<!-- Declared here, and render() puts these on the kind row — the row they
|
|
274
|
+
belong to is generated, so this is the only place they can be written
|
|
275
|
+
down. Each reads as the setting it would change, so what it shows IS the
|
|
276
|
+
current state: changing the word is the whole action, with no save. -->
|
|
277
|
+
<span id="moderation" hidden>
|
|
278
|
+
<label for="joins-mod">join requests</label>
|
|
279
|
+
<select id="joins-mod" title=" Whether each join request waits for you">
|
|
280
|
+
<option value="unmoderated">unmoderated</option>
|
|
281
|
+
<option value="moderated">moderated</option>
|
|
282
|
+
</select>
|
|
283
|
+
<label for="review-mod">posts</label>
|
|
284
|
+
<select id="review-mod" title=" Whether members’ posts wait for you">
|
|
285
|
+
<option value="unmoderated">unmoderated</option>
|
|
286
|
+
<option value="moderated">moderated</option>
|
|
287
|
+
</select>
|
|
288
|
+
</span>
|
|
289
|
+
<span id="status-ctl" hidden>
|
|
290
|
+
<select id="status-pick" aria-label="Actor status" title=" Whether this actor is receiving mail — parking stops it and keeps the handle; active re-follows the saved graph">
|
|
291
|
+
<option value="active">active</option>
|
|
292
|
+
<option value="parked">parked</option>
|
|
293
|
+
</select>
|
|
294
|
+
</span>
|
|
295
|
+
<span id="update-ctl" hidden>
|
|
296
|
+
<span id="update-word"></span>
|
|
297
|
+
<button id="update-go" hidden title=" Pull the latest FediPod and restart every agent on this machine">Update</button>
|
|
298
|
+
</span>
|
|
299
|
+
<span id="follows-ctl" hidden>
|
|
300
|
+
<select id="follows-pick" aria-label="New followers" title=" Whether each new follower waits for you — switch to accepted automatically before moving an old account here, so the whole wave lands">
|
|
301
|
+
<option value="approve">wait for approval</option>
|
|
302
|
+
<option value="auto">accepted automatically</option>
|
|
303
|
+
</select>
|
|
304
|
+
</span>
|
|
305
|
+
<span id="bsky-connect-ctl" hidden>
|
|
306
|
+
<button type="button" id="bsky-open" class="inline" title=" Connect a Bluesky account this identity also posts as">Connect Bluesky account</button>
|
|
307
|
+
</span>
|
|
308
|
+
<span id="bsky-ctl" hidden>
|
|
309
|
+
<select id="bsky-crosspost" aria-label="Bluesky crossposting" title=" Whether your public posts also go to the Bluesky account">
|
|
310
|
+
<option value="on">crosspost on</option>
|
|
311
|
+
<option value="off">crosspost off</option>
|
|
312
|
+
</select>
|
|
313
|
+
<button type="button" id="bsky-disconnect" class="inline danger" title=" Forget the Bluesky session and remove the stored credential">Disconnect</button>
|
|
314
|
+
</span>
|
|
315
|
+
</section>
|
|
316
|
+
|
|
317
|
+
<!-- The inbox-gateway / multi-user-front controls are deliberately not shown
|
|
318
|
+
in the UI yet. The backend routes (GET/POST /gateway) exist and work; the
|
|
319
|
+
operator surface is parked until the feature is ready to expose. -->
|
|
320
|
+
|
|
321
|
+
<section id="pane-group" hidden>
|
|
322
|
+
<!-- Both queues are consequences of a moderation setting: with the setting off
|
|
323
|
+
nothing can ever arrive in them, so the heading would be a standing
|
|
324
|
+
promise of a list that cannot fill. Shown only when the setting is on AND
|
|
325
|
+
something is waiting. -->
|
|
326
|
+
<div id="block-requests" hidden>
|
|
327
|
+
<h3>Follow requests <span class="muted" id="requests-count"></span>
|
|
328
|
+
<button id="admit-all" class="inline" title=" Accept everyone waiting, in one go">Accept all</button></h3>
|
|
329
|
+
<ul class="rows" role="list" id="requests"></ul>
|
|
330
|
+
</div>
|
|
331
|
+
|
|
332
|
+
<div id="block-pending" hidden>
|
|
333
|
+
<h3>Posts to review <span class="muted" id="pending-count"></span></h3>
|
|
334
|
+
<ul class="rows" role="list" id="pending"></ul>
|
|
335
|
+
</div>
|
|
336
|
+
|
|
337
|
+
<!-- Moderation another server asked for. A delivery does not prove who sent
|
|
338
|
+
it, so a moderator's request waits to be vouched for rather than acting
|
|
339
|
+
on arrival. Shown only when something is actually waiting. -->
|
|
340
|
+
<div id="block-modqueue" hidden>
|
|
341
|
+
<h3>Moderation requests <span class="muted" id="modqueue-count"></span></h3>
|
|
342
|
+
<p class="hint">Asked for by a moderator over the fediverse. A delivery cannot prove who
|
|
343
|
+
sent it, so nothing here has been acted on.</p>
|
|
344
|
+
<ul class="rows" role="list" id="modqueue"></ul>
|
|
345
|
+
</div>
|
|
346
|
+
|
|
347
|
+
<h3>Members <span class="muted" id="members-count"></span></h3>
|
|
348
|
+
<p class="hint">A group cannot force an unfollow. Muting is declining to carry; ejecting also
|
|
349
|
+
tells their server it is no longer a member.</p>
|
|
350
|
+
<ul class="rows" role="list" id="members"></ul>
|
|
351
|
+
|
|
352
|
+
<h3>Carried <span class="muted" id="announced-count"></span></h3>
|
|
353
|
+
<ul class="rows" role="list" id="announced"></ul>
|
|
354
|
+
</section>
|
|
355
|
+
|
|
356
|
+
<section id="pane-inbox" hidden>
|
|
357
|
+
<h2><span class="hlabel">Inbox</span></h2>
|
|
358
|
+
<p id="inbox-summary"></p>
|
|
359
|
+
<p class="hint">Your pod accepts deliveries whether or not this agent is running, so a
|
|
360
|
+
machine that has been off collects them. The agent is working through them oldest first
|
|
361
|
+
and will get there on its own — this is only here if you would rather not wait.</p>
|
|
362
|
+
<p>
|
|
363
|
+
<button id="inbox-keep" title=" Keep everything and let the agent work through it">Keep everything</button>
|
|
364
|
+
<label for="inbox-before">or discard content older than</label>
|
|
365
|
+
<select id="inbox-before">
|
|
366
|
+
<option value="7">7 days</option>
|
|
367
|
+
<option value="30" selected>30 days</option>
|
|
368
|
+
<option value="90">90 days</option>
|
|
369
|
+
</select>
|
|
370
|
+
<button id="inbox-prune" title=" Discard content older than the chosen age; keep the bookkeeping" class="primary">Discard it</button>
|
|
371
|
+
</p>
|
|
372
|
+
<p class="hint">Discarding drops posts, not bookkeeping: follows, unfollows, acceptances
|
|
373
|
+
and deletions in that period are still applied, so your follower list stays right and
|
|
374
|
+
nothing stays up that its author took down. What goes is the timeline you would have
|
|
375
|
+
read that week.</p>
|
|
376
|
+
<p class="err" id="inbox-warn" hidden></p>
|
|
377
|
+
</section>
|
|
378
|
+
|
|
379
|
+
<!-- Every form and panel is DECLARED here, inside the window that shows them.
|
|
380
|
+
Nothing is built in script — window.js only unhides one at a time, moves
|
|
381
|
+
the window and closes it. It floats rather than opening at the foot of the
|
|
382
|
+
page, so it never pushes the line you are reading out from under you. -->
|
|
383
|
+
<div id="win" hidden>
|
|
384
|
+
<div id="win-bar"><span id="win-title"></span><button type="button" id="win-close" title=" Close this panel" aria-label="Close panel">✕</button></div>
|
|
385
|
+
<div id="win-body">
|
|
386
|
+
<pre id="output" tabindex="0" role="region" aria-label="Output" hidden></pre>
|
|
387
|
+
<!-- The inbound half of transferring: the aliases its old server checks before
|
|
388
|
+
it will send the Move. The outbound half is its own panel (confirm-form,
|
|
389
|
+
warn-move) — additive and reversible here, followers handed away there,
|
|
390
|
+
so the two directions never share a door. -->
|
|
391
|
+
<div id="transfer-in-form" hidden>
|
|
392
|
+
<p class="hint">List the old account here first — its server checks this list before it
|
|
393
|
+
will send the move. Then start the transfer from the old account's own settings, and
|
|
394
|
+
set new followers to "accepted automatically" so the whole wave lands.</p>
|
|
395
|
+
<p id="alias-chips"></p>
|
|
396
|
+
<span id="alias-ctl">
|
|
397
|
+
<input type="text" id="alias-input" placeholder="@you@old.server" autocomplete="off"
|
|
398
|
+
title=" The account you are moving here from; its server checks this list before it will send the Move">
|
|
399
|
+
<button type="button" id="alias-add" class="inline"
|
|
400
|
+
title=" Resolve the old account, list it in alsoKnownAs, and republish the actor">Add alias</button>
|
|
401
|
+
</span>
|
|
402
|
+
</div>
|
|
403
|
+
<form id="bsky-form" hidden>
|
|
404
|
+
<ol class="hint">
|
|
405
|
+
<li>Get an account on Bluesky or another ATProto server.</li>
|
|
406
|
+
<li>On Bluesky, make an app password (Settings → Privacy and security → App Passwords)
|
|
407
|
+
and copy it — it is shown only once.</li>
|
|
408
|
+
<li>Fill in this form to connect, pasting that app password into the password
|
|
409
|
+
field.</li>
|
|
410
|
+
</ol>
|
|
411
|
+
<label for="bsky-service">service</label>
|
|
412
|
+
<input type="url" id="bsky-service" value="https://bsky.social" autocomplete="off">
|
|
413
|
+
<label for="bsky-identifier">handle</label>
|
|
414
|
+
<input type="text" id="bsky-identifier" placeholder="you.bsky.social" autocomplete="username">
|
|
415
|
+
<label for="bsky-password">app password</label>
|
|
416
|
+
<input id="bsky-password" type="password" autocomplete="current-password">
|
|
417
|
+
<p>
|
|
418
|
+
<button id="bsky-connect" type="submit" class="primary" title=" Sign in to the Bluesky account with the app password">Connect</button>
|
|
419
|
+
</p>
|
|
420
|
+
</form>
|
|
421
|
+
<!-- Every consequence is written here rather than assembled in script: the
|
|
422
|
+
page asking "are you sure" has to say what it is sure about, and this is
|
|
423
|
+
the only record of it. Script picks which block to show, nothing else. -->
|
|
424
|
+
<form id="confirm-form" hidden>
|
|
425
|
+
<div id="warn-rotate-key" class="warn" hidden>
|
|
426
|
+
<p>A new keypair replaces the one in this home and the actor is republished so other
|
|
427
|
+
servers learn it. Any other device still holding the old key stops being able to sign.</p>
|
|
428
|
+
</div>
|
|
429
|
+
<div id="warn-retire" class="warn" hidden>
|
|
430
|
+
<p><strong>This cannot be undone.</strong> A Delete goes to every follower's inbox telling
|
|
431
|
+
those servers to drop the account and the posts they cached, the actor document is
|
|
432
|
+
replaced with a Tombstone, and this agent will refuse to start again for this pod. Your
|
|
433
|
+
posts and RDF stay on the pod; the identity does not come back.</p>
|
|
434
|
+
<p>Two things that are not this, and can be done from here:
|
|
435
|
+
<button type="button" id="go-park" title=" Park instead: keeps the handle, just stops the mail" class="inline">Park it instead</button>
|
|
436
|
+
keeps the handle resolving and just stops the mail — setting status back to
|
|
437
|
+
active undoes it.
|
|
438
|
+
<button type="button" id="go-move" title=" Transfer instead: hands your followers to another account" class="inline">Transfer it instead</button>
|
|
439
|
+
hands your followers to another account.</p>
|
|
440
|
+
<label for="confirm-handle">Type the handle to confirm</label>
|
|
441
|
+
<input type="text" id="confirm-handle" autocomplete="off">
|
|
442
|
+
</div>
|
|
443
|
+
<div id="warn-move-state" class="warn" hidden>
|
|
444
|
+
<p>Your timeline, contacts, blocklist and notifications move to the place you choose.
|
|
445
|
+
Everything is copied and checked before the agent switches over, and the old copy is
|
|
446
|
+
left where it was for you to delete. The agent keeps running.</p>
|
|
447
|
+
<fieldset>
|
|
448
|
+
<legend>Where to</legend>
|
|
449
|
+
<label class="choice"><input type="radio" name="stateWhere" value="path" checked>
|
|
450
|
+
another place on this device</label>
|
|
451
|
+
<label class="choice"><input type="radio" name="stateWhere" value="url">
|
|
452
|
+
another device</label>
|
|
453
|
+
<label class="choice"><input type="radio" name="stateWhere" value="pod">
|
|
454
|
+
onto the pod</label>
|
|
455
|
+
<div id="state-row-path">
|
|
456
|
+
<label for="state-path">Directory</label>
|
|
457
|
+
<input type="text" id="state-path" autocomplete="off" placeholder="~/somewhere/private/">
|
|
458
|
+
</div>
|
|
459
|
+
<div id="state-row-url" hidden>
|
|
460
|
+
<label for="state-url">Address the other device serves</label>
|
|
461
|
+
<input type="url" id="state-url" autocomplete="off" placeholder="https://other-device.example/private/">
|
|
462
|
+
</div>
|
|
463
|
+
</fieldset>
|
|
464
|
+
</div>
|
|
465
|
+
<div id="warn-move" class="warn" hidden>
|
|
466
|
+
<p>A <code>Move</code> goes to every follower's inbox and their servers migrate them to the
|
|
467
|
+
account you name, so they end up following that one instead. The actor left behind
|
|
468
|
+
advertises <code>movedTo</code>, so this handle keeps resolving as a redirect rather than
|
|
469
|
+
breaking — nothing here is deleted.</p>
|
|
470
|
+
<p>Then this identity is parked, exactly as Park would: everyone it follows is unfollowed
|
|
471
|
+
and the inbox is closed. Migrating is the far end's decision, so some followers will not
|
|
472
|
+
come across.</p>
|
|
473
|
+
<label for="move-target">Transfer to</label>
|
|
474
|
+
<input type="text" id="move-target" autocomplete="off" placeholder="@you@elsewhere or an actor URL">
|
|
475
|
+
<label for="confirm-handle-move">Type this actor's handle to confirm</label>
|
|
476
|
+
<input type="text" id="confirm-handle-move" autocomplete="off">
|
|
477
|
+
</div>
|
|
478
|
+
<p>
|
|
479
|
+
<button type="submit" id="confirm-go" title=" Do it" class="primary">Confirm</button>
|
|
480
|
+
<button type="button" id="confirm-cancel" title=" Change nothing and close">Cancel</button>
|
|
481
|
+
</p>
|
|
482
|
+
</form>
|
|
483
|
+
</div>
|
|
484
|
+
</div>
|
|
485
|
+
|
|
486
|
+
</div>
|
|
487
|
+
</main>
|
|
488
|
+
|
|
489
|
+
<script src="bar.js"></script>
|
|
490
|
+
<script src="window.js"></script>
|
|
491
|
+
<script src="admin.js"></script>
|
|
492
|
+
</body>
|
|
493
|
+
</html>
|