Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38908b9a25 | ||
|
|
08b0ee013f | ||
|
|
5d60bd801c | ||
|
|
d896d3d068 | ||
|
|
1cba4e8649 | ||
|
|
f6077f0063 | ||
|
|
315a89380f | ||
|
|
4dc8fcfa9c | ||
|
|
759c284b26 | ||
|
|
4ae184f24c |
@@ -8,6 +8,7 @@ APP_HEALTHCHECK_TIMEOUT=5s
|
||||
APP_HEALTHCHECK_START_PERIOD=10s
|
||||
APP_HEALTHCHECK_RETRIES=3
|
||||
LINKLOG_APP_NAME=LinkLog
|
||||
LINKLOG_VERSION=0.1.0
|
||||
LINKLOG_SECRET_KEY=replace-with-a-long-random-secret
|
||||
LINKLOG_TOKEN_EXPIRY_DAYS=30
|
||||
# Optional comma-separated override. Leave empty to use the built-in list.
|
||||
@@ -15,14 +16,3 @@ LINKLOG_TRACKING_PARAMS=
|
||||
# Keep the default path when using the named linklog_data volume.
|
||||
LINKLOG_DATABASE_PATH=/app/backend/data/linklog.db
|
||||
|
||||
# Traefik
|
||||
TRAEFIK_IMAGE=traefik:v3.1
|
||||
TRAEFIK_CONTAINER_NAME=linklog-traefik
|
||||
TRAEFIK_HOST=localhost
|
||||
TRAEFIK_HTTP_PORT=80
|
||||
TRAEFIK_HTTP_INTERNAL_PORT=80
|
||||
TRAEFIK_DASHBOARD_PORT=8080
|
||||
TRAEFIK_DASHBOARD_BIND_ADDRESS=127.0.0.1
|
||||
TRAEFIK_API_INSECURE=true
|
||||
TRAEFIK_RESTART_POLICY=unless-stopped
|
||||
DOCKER_SOCKET_PATH=/var/run/docker.sock
|
||||
|
||||
@@ -0,0 +1,90 @@
|
||||
name: Release LinkLog
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*'
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
IMAGE_NAME: git.kolkman.org/olaf/link-log
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out release tag
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Validate versions and signed XPI
|
||||
id: release
|
||||
run: |
|
||||
python3 scripts/release/validate_release.py
|
||||
version=$(python3 -c "import json; print(json.load(open('frontend/version.json'))['version'])")
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
if [ "${GITHUB_REF_NAME#v}" != "$version" ]; then
|
||||
echo "tag ${GITHUB_REF_NAME} does not match release version $version" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Log in to Gitea container registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: git.kolkman.org
|
||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
|
||||
- name: Build and publish Docker image
|
||||
uses: docker/build-push-action@v6
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: |
|
||||
${{ env.IMAGE_NAME }}:${{ steps.release.outputs.version }}
|
||||
${{ env.IMAGE_NAME }}:latest
|
||||
labels: |
|
||||
org.opencontainers.image.version=${{ steps.release.outputs.version }}
|
||||
org.opencontainers.image.source=https://git.kolkman.org/olaf/Link-Log
|
||||
|
||||
- name: Create Gitea release
|
||||
id: gitea_release
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
response=$(curl --fail-with-body --silent --show-error \
|
||||
-X POST \
|
||||
-H "Authorization: token $GITEA_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
-d "{\"tag_name\":\"v$VERSION\",\"name\":\"LinkLog $VERSION\",\"draft\":false,\"prerelease\":false}" \
|
||||
https://git.kolkman.org/api/v1/repos/olaf/Link-Log/releases)
|
||||
release_id=$(printf '%s' "$response" | jq -r '.id')
|
||||
test "$release_id" != null
|
||||
test "$release_id" != 0
|
||||
upload_url="https://git.kolkman.org/api/v1/repos/olaf/Link-Log/releases/$release_id/assets"
|
||||
echo "upload_url=$upload_url" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Upload signed XPI and update manifest
|
||||
env:
|
||||
GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }}
|
||||
UPLOAD_URL: ${{ steps.gitea_release.outputs.upload_url }}
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
curl --fail-with-body --silent --show-error \
|
||||
-X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H 'Content-Type: application/x-xpinstall' \
|
||||
--data-binary "@XPI/signed/LinkLog-$VERSION.xpi" \
|
||||
"$UPLOAD_URL?name=LinkLog-$VERSION.xpi"
|
||||
curl --fail-with-body --silent --show-error \
|
||||
-X POST -H "Authorization: token $GITEA_TOKEN" \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-binary @webextension/updates.json \
|
||||
"$UPLOAD_URL?name=updates.json"
|
||||
|
||||
- name: Publish release links
|
||||
env:
|
||||
VERSION: ${{ steps.release.outputs.version }}
|
||||
run: |
|
||||
echo "Docker image: $IMAGE_NAME:$VERSION"
|
||||
echo "Signed XPI: https://git.kolkman.org/olaf/Link-Log/releases/download/v$VERSION/LinkLog-$VERSION.xpi"
|
||||
echo "Firefox update manifest: https://git.kolkman.org/olaf/Link-Log/raw/branch/main/webextension/updates.json"
|
||||
@@ -15,3 +15,5 @@ __pycache__/
|
||||
*.sqlite3
|
||||
*.log
|
||||
backend/data/avatars/
|
||||
LinkLog.afdesign~lock~
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
FROM python:3.11-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
LinkLog is licensed under the GNU General Public License, version 3 or any later version (GPL-3.0-or-later).
|
||||
|
||||
Copyright (C) 2026 Olaf Kolkman
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
The complete license text is available at:
|
||||
https://www.gnu.org/licenses/gpl-3.0.html
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
||||
<g transform="matrix(0.972876,0,0,1.26057,-24.1588,-587.485)">
|
||||
<rect x="712.491" y="669.041" width="1854.11" height="1357.72" style="fill:rgb(88,91,112);"/>
|
||||
</g>
|
||||
<g transform="matrix(5.14628,0,0,6.12686,41.2191,-8673.98)">
|
||||
<path d="M141.95,1644.94L141.95,1633.94C157.554,1633.4 169.724,1631.04 178.462,1626.86C187.2,1622.69 193.344,1615.8 196.894,1606.21C200.444,1596.61 202.218,1583.47 202.218,1566.77C202.218,1554.44 201.146,1544.16 199,1535.93C196.855,1527.7 193.285,1521.25 188.292,1516.57C185.016,1513.6 181.154,1511.26 176.707,1509.54C172.26,1507.83 167.15,1506.62 161.376,1505.92C155.603,1505.21 149.128,1504.86 141.95,1504.86L141.95,1493.86L384.076,1493.86L384.076,1504.86C373.388,1504.86 364.221,1505.7 356.575,1507.38C348.929,1509.06 342.708,1512.12 337.909,1516.57C333.111,1521.01 329.581,1527.27 327.319,1535.35C325.056,1543.42 323.925,1553.9 323.925,1566.77L323.925,1633.36C339.06,1633.36 353.532,1631.08 367.341,1626.51C381.15,1621.95 393.399,1616 404.087,1608.66C414.776,1601.41 423.299,1593.3 429.657,1584.32C436.016,1575.35 439.546,1566.61 440.248,1558.11L452.536,1558.11C452.536,1562.4 452.419,1568.35 452.185,1575.96C451.951,1583.56 451.6,1591.85 451.132,1600.82C450.586,1609.95 449.942,1618.34 449.201,1625.99C448.46,1633.63 447.582,1639.95 446.568,1644.94L141.95,1644.94Z" style="fill:rgb(245,224,220);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.135097,0,0,0.266653,668.366,1282.68)">
|
||||
<path d="M846.315,1802.1L846.315,1747.02C926.615,1744.29 989.248,1732.47 1034.22,1711.57C1079.18,1690.67 1110.8,1656.2 1129.07,1608.15C1147.34,1560.11 1156.47,1494.29 1156.47,1410.69C1156.47,1348.97 1150.95,1297.51 1139.91,1256.3C1128.87,1215.09 1110.5,1182.76 1084.81,1159.33C1067.94,1144.48 1048.07,1132.76 1025.18,1124.17C1002.3,1115.58 975.999,1109.52 946.288,1106.01C916.577,1102.49 883.253,1100.73 846.315,1100.73L846.315,1045.65L2092.36,1045.65L2092.36,1100.73C2037.36,1100.73 1990.18,1104.93 1950.83,1113.33C1911.49,1121.73 1879.47,1137.06 1854.78,1159.33C1830.08,1181.59 1811.92,1212.94 1800.27,1253.37C1788.63,1293.8 1782.81,1346.24 1782.81,1410.69L1782.81,1744.09C1860.7,1744.09 1935.18,1732.66 2006.24,1709.81C2077.31,1686.96 2140.34,1657.18 2195.35,1620.46C2250.35,1584.13 2294.21,1543.51 2326.94,1498.58C2359.66,1453.66 2377.83,1409.91 2381.44,1367.33L2444.68,1367.33C2444.68,1388.82 2444.07,1418.6 2442.87,1456.69C2441.66,1494.78 2439.86,1536.28 2437.45,1581.2C2434.64,1626.9 2431.33,1668.9 2427.51,1707.18C2423.7,1745.46 2419.18,1777.1 2413.96,1802.1L846.315,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3043.37,1093.7C3001.22,1093.7 2961.37,1089.99 2923.83,1082.57C2886.29,1075.15 2853.66,1064.99 2825.96,1052.1C2797.86,1039.21 2775.67,1024.17 2759.41,1006.98C2743.15,989.794 2735.02,971.435 2735.02,951.904C2735.02,924.951 2748.97,900.732 2776.88,879.247C2804.78,857.763 2842.12,840.673 2888.9,827.978C2935.67,815.283 2987.16,808.935 3043.37,808.935C3101.59,808.935 3153.98,815.38 3200.56,828.271C3247.13,841.161 3284.07,858.447 3311.37,880.126C3338.67,901.806 3352.32,925.732 3352.32,951.904C3352.32,977.294 3338.67,1000.83 3311.37,1022.51C3284.07,1044.19 3247.13,1061.47 3200.56,1074.37C3153.98,1087.26 3101.59,1093.7 3043.37,1093.7ZM2498.94,1802.1L2498.94,1747.02C2608.95,1743.12 2686.24,1720.16 2730.81,1678.17C2775.37,1636.18 2797.66,1576.9 2797.66,1500.34C2797.66,1422.22 2776.08,1365.97 2732.91,1331.59C2689.75,1297.22 2618.39,1280.03 2518.82,1280.03L2518.82,1224.95L3327.03,1172.22L3327.03,1500.34C3327.03,1551.12 3333.76,1594.38 3347.2,1630.13C3360.66,1665.87 3386.05,1693.6 3423.39,1713.33C3460.73,1733.06 3515.53,1744.29 3587.8,1747.02L3587.8,1802.1L2498.94,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3625.19,1802.1L3625.19,1747.02C3724.76,1743.12 3796.43,1721.83 3840.19,1683.15C3862.68,1663.62 3878.94,1639.6 3888.97,1611.08C3899.01,1582.57 3904.03,1549.17 3904.03,1510.89C3904.03,1447.22 3894.7,1399.37 3876.03,1367.33C3857.36,1335.3 3827.64,1313.72 3786.89,1302.59C3746.14,1291.45 3692.24,1285.89 3625.19,1285.89L3625.19,1230.81L4433.4,1178.08L4433.4,1274.76C4505.67,1243.51 4581.56,1216.94 4661.05,1195.07C4740.55,1173.19 4822.65,1162.26 4907.37,1162.26C4940.29,1162.26 4973.62,1164.31 5007.34,1168.41C5041.07,1172.51 5073.19,1183.93 5103.7,1202.68C5122.98,1214.79 5139.84,1231.2 5154.29,1251.9C5168.34,1273 5179.59,1299.17 5188.02,1330.42C5196.45,1361.67 5200.66,1399.37 5200.66,1443.51L5199.46,1488.62C5198.26,1504.25 5197.65,1521.24 5197.65,1539.6C5197.65,1573.97 5199.46,1604.15 5203.07,1630.13C5206.69,1656.1 5215.92,1677.68 5230.78,1694.87C5245.23,1712.06 5267.51,1725.05 5297.63,1733.84C5327.74,1742.63 5369.29,1747.02 5422.29,1747.02L5422.29,1802.1L4575.53,1802.1L4575.53,1757.57C4601.63,1741.16 4619.7,1713.62 4629.74,1674.95C4639.77,1636.28 4644.79,1583.35 4644.79,1516.16C4644.79,1457.96 4639.47,1415.09 4628.83,1387.55C4618.19,1360.01 4603.74,1342.14 4585.47,1333.93C4567.2,1325.73 4546.83,1321.63 4524.34,1321.63C4510.29,1321.63 4495.54,1323.1 4480.08,1326.03C4464.62,1328.95 4449.06,1332.96 4433.4,1338.04L4433.4,1526.71C4433.4,1595.07 4438.52,1646.53 4448.76,1681.1C4459,1715.67 4476.97,1741.16 4502.66,1757.57L4502.66,1802.1L3625.19,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M6760.3,1835.5C6693.65,1835.5 6638.55,1833.06 6594.99,1828.17C6551.42,1823.29 6518.2,1814.4 6495.32,1801.51C6481.26,1793.7 6469.62,1783.35 6460.39,1770.46C6451.55,1757.96 6444.73,1743.31 6439.91,1726.51C6435.09,1709.72 6430.67,1690.38 6426.66,1668.51C6422.64,1647.41 6416.92,1627.68 6409.49,1609.33C6402.07,1590.97 6387.21,1575.83 6364.93,1563.91C6342.65,1552 6306.81,1545.85 6257.43,1545.46C6258.23,1615.38 6265.66,1666.45 6279.71,1698.68C6293.76,1730.91 6320.86,1747.02 6361.01,1747.02L6361.01,1802.1L5449.21,1802.1L5449.21,1747.02C5485.35,1745.85 5517.27,1742.92 5544.97,1738.23C5600.78,1728.86 5642.33,1707.96 5669.64,1675.54C5683.69,1658.35 5695.13,1636.87 5703.96,1611.08C5712.4,1585.3 5718.52,1554.15 5722.33,1517.63C5726.15,1481.1 5728.05,1438.43 5728.05,1389.6C5728.05,1315.38 5722.84,1255.22 5712.4,1209.13C5701.96,1163.04 5685.49,1127.1 5663.01,1101.32C5640.53,1075.93 5611.72,1058.74 5576.59,1049.76C5541.46,1040.77 5499,1036.28 5449.21,1036.28L5449.21,981.786L6257.43,929.052L6257.43,1462.84C6286.74,1442.92 6314.84,1422.12 6341.74,1400.44C6368.64,1378.76 6392.33,1358.54 6412.81,1339.79C6457.78,1298.39 6480.26,1272.8 6480.26,1263.04C6480.26,1255.22 6471.63,1250.15 6454.36,1247.8C6437.1,1245.46 6413.61,1244.29 6383.9,1244.29L6383.9,1189.21L7053.6,1189.21L7053.6,1244.29C7005.82,1244.29 6953.42,1249.76 6896.41,1260.69C6839.4,1271.63 6778.97,1288.04 6715.14,1309.91C6650.9,1331.79 6583.85,1359.33 6513.99,1392.53C6444.12,1425.73 6372.46,1464.6 6298.98,1509.13C6373.66,1497.41 6449.04,1488.13 6525.13,1481.3C6601.21,1474.46 6668.36,1471.04 6726.58,1471.04C6810.89,1471.04 6875.33,1480.42 6919.9,1499.17C6965.67,1518.7 6994.58,1550.34 7006.62,1594.09C7013.85,1619.87 7023.18,1641.94 7034.63,1660.3C7046.07,1678.66 7058.21,1693.9 7071.06,1706.01C7083.91,1718.12 7096.86,1727.2 7109.91,1733.25C7122.96,1739.31 7134.3,1742.92 7143.93,1744.09L7143.93,1805.62C7133.49,1808.74 7116.23,1811.96 7092.14,1815.28C7068.05,1818.6 7039.14,1821.83 7005.42,1824.95C6971.29,1828.08 6933.35,1830.62 6891.59,1832.57C6849.84,1834.52 6806.07,1835.5 6760.3,1835.5Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M7746.18,1802.1L7746.18,1747.02C7826.48,1744.29 7889.11,1732.47 7934.08,1711.57C7979.05,1690.67 8010.67,1656.2 8028.93,1608.15C8047.2,1560.11 8056.34,1494.29 8056.34,1410.69C8056.34,1348.97 8050.82,1297.51 8039.77,1256.3C8028.73,1215.09 8010.36,1182.76 7984.67,1159.33C7967.81,1144.48 7947.93,1132.76 7925.05,1124.17C7902.16,1115.58 7875.86,1109.52 7846.15,1106.01C7816.44,1102.49 7783.12,1100.73 7746.18,1100.73L7746.18,1045.65L8992.23,1045.65L8992.23,1100.73C8937.22,1100.73 8890.05,1104.93 8850.7,1113.33C8811.35,1121.73 8779.33,1137.06 8754.64,1159.33C8729.95,1181.59 8711.78,1212.94 8700.14,1253.37C8688.49,1293.8 8682.67,1346.24 8682.67,1410.69L8682.67,1744.09C8760.56,1744.09 8835.04,1732.66 8906.1,1709.81C8977.17,1686.96 9040.2,1657.18 9095.21,1620.46C9150.22,1584.13 9194.08,1543.51 9226.8,1498.58C9259.52,1453.66 9277.69,1409.91 9281.3,1367.33L9344.54,1367.33C9344.54,1388.82 9343.94,1418.6 9342.73,1456.69C9341.53,1494.78 9339.72,1536.28 9337.31,1581.2C9334.5,1626.9 9331.19,1668.9 9327.38,1707.18C9323.56,1745.46 9319.05,1777.1 9313.83,1802.1L7746.18,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M10190.6,1828.47C10046.1,1828.47 9919.52,1816.36 9810.91,1792.14C9702.31,1767.92 9617.89,1731.2 9557.67,1681.98C9497.44,1632.76 9467.33,1570.65 9467.33,1495.65C9467.33,1439.01 9484.69,1389.6 9519.43,1347.41C9554.15,1305.22 9603.24,1270.46 9666.67,1243.12C9730.11,1215.38 9806.19,1194.87 9894.93,1181.59C9983.66,1168.31 10082.2,1161.67 10190.6,1161.67C10298.6,1161.67 10397.2,1168.31 10486.3,1181.59C10575.5,1194.87 10651.5,1215.38 10714.6,1243.12C10778,1270.46 10827,1305.22 10861.5,1347.41C10896.1,1389.6 10913.3,1439.01 10913.3,1495.65C10913.3,1551.9 10896.1,1600.83 10861.5,1642.43C10827,1684.03 10778,1718.7 10714.6,1746.43C10651.5,1773.78 10575.5,1794.29 10486.3,1807.96C10397.2,1821.63 10298.6,1828.47 10190.6,1828.47ZM10190.6,1766.94C10209.5,1766.94 10226.5,1759.62 10241.5,1744.97C10256.6,1730.32 10269.5,1710.5 10280.4,1685.5C10302,1634.72 10312.9,1571.43 10312.9,1495.65C10312.9,1419.09 10302,1355.22 10280.4,1304.05C10269.5,1279.05 10256.6,1259.13 10241.5,1244.29C10226.5,1229.44 10209.5,1222.02 10190.6,1222.02C10171.4,1222.02 10154.2,1229.44 10139.1,1244.29C10124.1,1259.13 10111.3,1279.05 10100.9,1304.05C10090.1,1329.05 10081.8,1358.06 10076.2,1391.06C10070.6,1424.07 10067.8,1458.93 10067.8,1495.65C10067.8,1531.98 10070.6,1566.55 10076.2,1599.37C10081.8,1632.18 10090.1,1660.89 10100.9,1685.5C10111.3,1710.5 10124.1,1730.32 10139.1,1744.97C10154.2,1759.62 10171.4,1766.94 10190.6,1766.94Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M11680.5,2098C11591.8,2098 11505.8,2096.73 11422.7,2094.19C11339.6,2091.65 11261.9,2085.5 11189.7,2075.73C11166.4,2072.61 11144.8,2069.09 11124.9,2065.18C11105,2061.28 11086.5,2056.79 11069.2,2051.71C11035.1,2041.55 11008.3,2029.35 10988.8,2015.09C10969.3,2000.83 10959.6,1983.54 10959.6,1963.23C10959.6,1947.22 10967.4,1932.86 10983.1,1920.16C10998.7,1907.47 11020,1896.43 11046.9,1887.06C11073.4,1877.68 11104.1,1869.58 11139.1,1862.74C11174,1855.91 11210.7,1850.34 11249.3,1846.04C11216.4,1832.37 11189.8,1815.87 11169.5,1796.53C11149.2,1777.2 11139.1,1755.22 11139.1,1730.62C11139.1,1713.43 11144.3,1697.51 11154.7,1682.86C11165.2,1668.21 11179.4,1654.83 11197.5,1642.72C11233.2,1618.51 11279.6,1600.34 11336.6,1588.23C11278.4,1578.86 11227.2,1565.77 11183,1548.97C11138.9,1532.18 11104.2,1511.18 11079.1,1485.99C11054.1,1460.79 11041.5,1431.01 11041.5,1396.63C11041.5,1355.62 11059.5,1319.97 11095.4,1289.7C11131.3,1259.42 11179,1235.11 11238.4,1216.75C11297.5,1198.39 11365.3,1184.72 11442,1175.73C11518.7,1166.75 11599.2,1162.26 11683.5,1162.26C11753.4,1162.26 11820.9,1165.28 11886.2,1171.34C11951.4,1177.39 12011.7,1187.06 12067.1,1200.34C12080.8,1169.48 12099,1145.46 12121.9,1128.27C12144.8,1111.08 12170.6,1098.68 12199.3,1091.06C12228,1083.45 12257.7,1078.76 12288.5,1077C12319.2,1075.24 12349.2,1074.37 12378.5,1074.37C12397.4,1074.37 12418.1,1074.56 12440.8,1074.95C12463.5,1075.34 12488.3,1076.32 12515.2,1077.88C12512.8,1088.82 12510.6,1103.86 12508.6,1123C12506.6,1142.14 12501.5,1159.13 12493.5,1173.97C12484.7,1190.38 12467.6,1201.71 12442.3,1207.96C12417,1214.21 12393.1,1217.33 12370.7,1217.33L12230.9,1217.33C12215.7,1217.33 12200.9,1217.63 12186.7,1218.21C12172.4,1218.8 12159.1,1220.46 12146.6,1223.19C12200.4,1241.55 12243.7,1265.09 12276.4,1293.8C12309.1,1322.51 12325.5,1356.79 12325.5,1396.63C12325.5,1438.04 12307.6,1472.7 12271.9,1500.63C12236.2,1528.56 12188.6,1550.54 12129.2,1566.55C12069.3,1582.57 12001.4,1594.09 11925.3,1601.12C11849.2,1608.15 11768.6,1611.67 11683.5,1611.67C11638.5,1611.67 11594.4,1610.79 11551,1609.03C11507.6,1607.28 11465.7,1604.25 11425.1,1599.95L11425.7,1602.88C11413.7,1602.88 11402.2,1606.4 11391.4,1613.43C11380.6,1620.46 11375.1,1629.25 11375.1,1639.79C11375.1,1649.17 11380.5,1657.18 11391.1,1663.82C11401.7,1670.46 11415.7,1675.73 11433,1679.64C11450.6,1683.54 11470.8,1686.38 11493.5,1688.13C11516.2,1689.89 11540,1690.77 11564.9,1690.77L11884,1690.77C11957.1,1690.77 12026.9,1691.06 12093.3,1691.65C12159.8,1692.24 12222.1,1697.61 12280.3,1707.76C12334.1,1716.75 12377.9,1733.25 12411.6,1757.28C12445.3,1781.3 12462.2,1815.58 12462.2,1860.11C12462.2,1897.61 12450.8,1929.44 12427.9,1955.62C12405,1981.79 12373.6,2003.56 12333.6,2020.95C12293.7,2038.33 12247.8,2052.29 12196,2062.84C12145,2073 12090.1,2080.62 12031.3,2085.69C11972.5,2090.77 11913.1,2094.09 11853,2095.65C11793,2097.22 11735.5,2098 11680.5,2098ZM11682.9,1547.22C11712.2,1547.22 11735.9,1539.79 11754,1524.95C11772,1510.11 11785.2,1490.58 11793.4,1466.36C11801.6,1442.14 11805.8,1415.77 11805.8,1387.26C11805.8,1341.94 11796.3,1303.76 11777.4,1272.7C11758.6,1241.65 11727.1,1226.12 11682.9,1226.12C11639.1,1226.12 11607.9,1241.65 11589.2,1272.7C11570.6,1303.76 11561.2,1341.94 11561.2,1387.26C11561.2,1415.38 11565.4,1441.65 11573.6,1466.06C11581.8,1490.48 11594.9,1510.11 11612.7,1524.95C11630.6,1539.79 11654,1547.22 11682.9,1547.22ZM11708.2,2035.3C11747.1,2035.3 11786.1,2034.62 11825,2033.25C11864,2031.88 11899.5,2028.66 11931.6,2023.58C11964.1,2018.9 11990.8,2011.87 12011.7,2002.49C12032.6,1993.12 12043,1980.62 12043,1964.99C12043,1947.02 12029.1,1933.25 12001.2,1923.68C11973.3,1914.11 11935.4,1906.98 11887.7,1902.29C11839.5,1897.61 11782.4,1894.78 11716.3,1893.8C11650.3,1892.82 11579.1,1892.33 11502.8,1892.33C11490.8,1892.33 11478.6,1891.94 11466.4,1891.16C11454.1,1890.38 11442,1889.4 11430,1888.23C11404.3,1905.81 11391.4,1929.44 11391.4,1959.13C11391.4,1974.76 11398.9,1987.55 11414,1997.51C11429,2007.47 11450.4,2015.18 11478.1,2020.65C11505.8,2026.12 11539.2,2029.93 11578.1,2032.08C11617.1,2034.23 11660.4,2035.3 11708.2,2035.3Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -0,0 +1,62 @@
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
SHELL := /bin/sh
|
||||
|
||||
SOURCE_LOGO := LinkLog.svg
|
||||
MAGICK ?= magick
|
||||
WEB_LOGO := frontend/static/logo.svg
|
||||
EXTENSION_LOGO := webextension/logo.svg
|
||||
ICON_FILES := webextension/icon-16.png webextension/icon-32.png webextension/icon-48.png webextension/icon-96.png
|
||||
GENERATED_LOGOS := $(WEB_LOGO) $(EXTENSION_LOGO) $(ICON_FILES)
|
||||
EXTENSION_VERSION := $(shell sed -n 's/^[[:space:]]*"version":[[:space:]]*"\([^"]*\)".*/\1/p' webextension/manifest.json | head -n 1)
|
||||
XPI_FILE := LinkLog-$(EXTENSION_VERSION).xpi
|
||||
XPI_UNSIGNED_DIR := XPI/unsigned
|
||||
XPI_SIGNED_DIR := XPI/signed
|
||||
XPI_OUTPUT := $(XPI_UNSIGNED_DIR)/$(XPI_FILE)
|
||||
EXTENSION_FILES := manifest.json logo.svg icon-16.png icon-32.png icon-48.png icon-96.png options.css options.html options.js popup.css popup.html popup.js
|
||||
|
||||
.PHONY: all logos xpi check-tools clean-generated
|
||||
|
||||
all: logos
|
||||
|
||||
|
||||
logos: check-tools $(GENERATED_LOGOS)
|
||||
|
||||
xpi: logos
|
||||
@test -n "$(EXTENSION_VERSION)" || { echo "Error: extension version is missing from webextension/manifest.json" >&2; exit 1; }
|
||||
@mkdir -p $(XPI_UNSIGNED_DIR) $(XPI_SIGNED_DIR)
|
||||
@rm -f $(XPI_OUTPUT)
|
||||
@cd webextension && zip -q -9 "../$(XPI_OUTPUT)" $(EXTENSION_FILES)
|
||||
@python3 scripts/release/validate_xpi.py "$(XPI_OUTPUT)" webextension/manifest.json
|
||||
@echo "Created $(XPI_OUTPUT)"
|
||||
|
||||
check-tools:
|
||||
@command -v $(MAGICK) >/dev/null 2>&1 || { echo "Error: ImageMagick '$(MAGICK)' is required. Install ImageMagick and retry." >&2; exit 1; }
|
||||
|
||||
$(WEB_LOGO): $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@cp $< $@
|
||||
|
||||
$(EXTENSION_LOGO): $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@cp $< $@
|
||||
|
||||
webextension/icon-16.png: $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@$(MAGICK) $< -resize 16x16 -gravity center -extent 16x16 $@
|
||||
|
||||
webextension/icon-32.png: $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@$(MAGICK) $< -resize 32x32 -gravity center -extent 32x32 $@
|
||||
|
||||
webextension/icon-48.png: $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@$(MAGICK) $< -resize 48x48 -gravity center -extent 48x48 $@
|
||||
|
||||
webextension/icon-96.png: $(SOURCE_LOGO)
|
||||
@mkdir -p $(@D)
|
||||
@$(MAGICK) $< -resize 96x96 -gravity center -extent 96x96 $@
|
||||
|
||||
clean-generated:
|
||||
@rm -f $(GENERATED_LOGOS)
|
||||
@@ -2,14 +2,17 @@
|
||||
|
||||
LinkLog is a Firefox extension and Python web service for saving links with a title, comment, timestamp, and tracking parameters removed. The service stores links in SQLite and can publish them through plugins, including Mastodon.
|
||||
|
||||
For full transparency: The author used vibe coding to create this software.
|
||||
|
||||
## Project Layout
|
||||
|
||||
```text
|
||||
backend/ FastAPI application, services, database, and tests
|
||||
frontend/ Jinja templates and browser-side assets
|
||||
webextension/ Firefox Manifest V3 extension
|
||||
Logo.svg Source logo artwork used by the web and extension interfaces
|
||||
Dockerfile Backend container image
|
||||
docker-compose.yml App plus Traefik for local proxying
|
||||
docker-compose.yml App with traefik reverse proxy hooks
|
||||
REQUIREMENTS.md Product requirements
|
||||
VIBE/ Conversation and prompt logs
|
||||
```
|
||||
@@ -24,6 +27,8 @@ For local development:
|
||||
|
||||
The backend currently uses FastAPI, uvicorn, SQLite, and Pydantic. `httpx2` is included for the Starlette-compatible test client.
|
||||
Jinja2 is included for server-rendered HTML templates.
|
||||
The backend version is `0.1.0` and is exposed through the FastAPI/OpenAPI metadata. It can be overridden with `LINKLOG_VERSION`.
|
||||
LinkLog is licensed under the GNU General Public License, version 3 or any later version. See [LICENSE](LICENSE).
|
||||
|
||||
## Local Installation
|
||||
|
||||
@@ -70,6 +75,7 @@ Open these URLs:
|
||||
- User feed: <http://localhost:8000/alice>
|
||||
- Profile settings: <http://localhost:8000/profile>
|
||||
- Labels: <http://localhost:8000/labels>
|
||||
- About: <http://localhost:8000/about>
|
||||
- Admin page: <http://localhost:8000/admin>
|
||||
- Web login: <http://localhost:8000/login>
|
||||
- Health check: <http://localhost:8000/health>
|
||||
@@ -77,13 +83,44 @@ Open these URLs:
|
||||
|
||||
The browser extension defaults to `http://localhost:8000`.
|
||||
|
||||
The supplied `Logo.svg` is bundled as `frontend/static/logo.svg` for web pages and `webextension/logo.svg` for the Firefox popup and settings page.
|
||||
The visible LinkLog brand text uses the Google Foundry `Asset` font when available, with local fallbacks in the Firefox extension.
|
||||
|
||||
## Regenerate Logo Assets
|
||||
|
||||
`LinkLog.svg` is the source logo. Install ImageMagick, then regenerate the web logo, extension logo, and Firefox toolbar icons with:
|
||||
|
||||
```sh
|
||||
make logos
|
||||
```
|
||||
|
||||
The generated files are `frontend/static/logo.svg`, `webextension/logo.svg`, and `webextension/icon-16.png`, `icon-32.png`, `icon-48.png`, and `icon-96.png`. Override the ImageMagick executable with `make MAGICK=magick logos` when needed.
|
||||
|
||||
Create an installable Firefox XPI bundle with:
|
||||
|
||||
```sh
|
||||
make xpi
|
||||
```
|
||||
|
||||
This creates `XPI/unsigned/LinkLog-0.1.0.xpi` from the `webextension/` package and excludes macOS metadata and minified artifacts. The version is read from `webextension/manifest.json`. The `XPI/signed/` directory is reserved for signed release bundles.
|
||||
|
||||
## Releases
|
||||
|
||||
Releases run in Gitea Actions when a `v*` tag is pushed. The Docker release version comes from `frontend/version.json`; the Firefox plugin version comes from `webextension/manifest.json`. CI also requires both to match `LINKLOG_VERSION`'s default in `backend/app/core/config.py`.
|
||||
|
||||
The signed XPI is produced manually and must be checked into `XPI/signed/LinkLog-<version>.xpi` before creating the tag. The workflow validates the embedded manifest, publishes the XPI and `webextension/updates.json` as Gitea release assets, and publishes Docker images to `git.kolkman.org/olaf/link-log:<version>` and `:latest`.
|
||||
|
||||
The extension's `update_url` points at the stable raw repository URL `https://git.kolkman.org/olaf/Link-Log/raw/branch/main/webextension/updates.json`. Update `webextension/updates.json` with each signed XPI version and commit it together with the XPI. The release page provides a direct install link at `https://git.kolkman.org/olaf/Link-Log/releases/download/v<version>/LinkLog-<version>.xpi`.
|
||||
|
||||
The workflow requires Gitea Actions secrets named `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and `GITEA_TOKEN`. The token needs permission to create releases and upload release assets; the registry credentials need permission to push the image.
|
||||
|
||||
Appending a username to the root URL, such as `/alice`, opens that user's public feed and profile information.
|
||||
|
||||
Configuration APIs require a bearer token returned by the login endpoint. User configuration uses the identity in that token. Plugin administration additionally requires an administrator account; the development `alice` account is seeded as an administrator, while `bob` is a standard user.
|
||||
|
||||
Users can change their password from the profile page. The current password is required, new passwords must contain at least 8 characters, and the endpoint is `PUT /api/user/password`.
|
||||
|
||||
On the profile page, the authenticated username is displayed as read-only. Users can upload a PNG, JPEG, GIF, or WebP avatar up to 2 MB; uploaded files are stored in the persistent data volume and served by the application. Bio and email fields remain empty until the user provides values. Mastodon settings default to the `mastodon.social` instance and the `From my #LinkLog: "` post prefix.
|
||||
On the profile page, the authenticated username is displayed as read-only. Users can upload a PNG, JPEG, GIF, or WebP avatar up to 2 MB; uploaded files are stored in the persistent data volume and served by the application. Bio and email fields remain empty until the user provides values. Mastodon settings default to the `mastodon.social` instance and the `From my #LinkLog: ` post prefix.
|
||||
|
||||
## Run Tests
|
||||
|
||||
@@ -101,6 +138,8 @@ PYTHONPATH=. PYTHONWARNINGS=error pytest backend/tests -q
|
||||
## Install The Firefox Extension For Development
|
||||
|
||||
The extension is an unpacked Firefox extension. No build step is required.
|
||||
The toolbar uses square PNG icons generated from the bundled dark-background logo; `logo.svg` remains available for the popup and settings branding.
|
||||
The manifest includes stable Firefox extension metadata and references the packaged PNG icons for toolbar and add-on installation.
|
||||
|
||||
1. Start the backend locally.
|
||||
2. Open Firefox and visit `about:debugging#/runtime/this-firefox`.
|
||||
@@ -113,9 +152,11 @@ The extension is an unpacked Firefox extension. No build step is required.
|
||||
6. Save the settings and login.
|
||||
7. Open a webpage, select the LinkLog toolbar button, review the title and URL, add a comment, and submit it.
|
||||
|
||||
When the extension settings page has a valid session, it shows `<username> logged in at <backend URL>` and a **Sign out** button instead of the login form. Signing out revokes the token and returns the form.
|
||||
|
||||
Temporary extensions are removed when Firefox restarts. Reload the extension from `about:debugging` after changing its files.
|
||||
|
||||
## Docker And Traefik
|
||||
## Docker
|
||||
|
||||
Create the Docker environment file before starting the stack:
|
||||
|
||||
@@ -132,12 +173,8 @@ The main configurable values are:
|
||||
| `LINKLOG_SECRET_KEY` | token signing/security secret | required in Docker |
|
||||
| `LINKLOG_DATABASE_PATH` | SQLite file path inside the container | `/app/backend/data/linklog.db` |
|
||||
| `LINKLOG_TOKEN_EXPIRY_DAYS` | access-token lifetime | `30` |
|
||||
| `LINKLOG_TRACKING_PARAMS` | comma-separated tracking parameters | built-in list |
|
||||
| `LINKLOG_TRACKING_PARAMS` | comma-separated tracking parameters (stripped from logged URLs) | built-in list |
|
||||
| `TRAEFIK_HOST` | hostname routed by Traefik | `localhost` |
|
||||
| `TRAEFIK_HTTP_PORT` | host port for the application proxy | `80` |
|
||||
| `TRAEFIK_DASHBOARD_PORT` | host port for the dashboard | `8080` |
|
||||
| `TRAEFIK_DASHBOARD_BIND_ADDRESS` | host address for the dashboard | `127.0.0.1` |
|
||||
| `TRAEFIK_API_INSECURE` | enable the local dashboard API | `true` |
|
||||
| `APP_PORT` | direct host port for FastAPI | `8000` |
|
||||
|
||||
The full set of supported variables is listed in `.env.example`. Application variables are passed into the container by Compose; Docker and Traefik variables are used by Compose itself.
|
||||
@@ -177,7 +214,7 @@ After logging in, open <http://localhost:8000/profile> and save the Mastodon set
|
||||
|
||||
- **Instance**: hostname or URL such as `mastodon.social` or `https://mastodon.social`
|
||||
- **Access token**: a Mastodon API token with permission to create statuses
|
||||
- **Post prefix**: text placed immediately before the link; defaults to `From my #LinkLog: "`
|
||||
- **Post prefix**: text placed immediately before the link; defaults to `From my #LinkLog: `
|
||||
|
||||
Enable the plugin from the admin API or the admin page. New links are saved first and then posted to the configured instance at `/api/v1/statuses`. A Mastodon network failure does not undo the saved link.
|
||||
|
||||
|
||||
@@ -373,3 +373,141 @@ Create a page where users can add, delete, or edit labels - Labels can be edited
|
||||
|
||||
### Assistant outcome
|
||||
Added schema migration 4 with label ownership, authenticated user label CRUD endpoints and a `/labels` management page. Label creators can edit/delete their own labels, non-owners are denied, and administrators can delete any label including another user's labels. Added administrator label listing/deletion controls and regression coverage. The strict backend suite passed with 15 tests.
|
||||
|
||||
### User
|
||||
The profile page shows in red: "can't access property \"classList\", document.querySelector(...) is null"
|
||||
|
||||
### Assistant outcome
|
||||
Found that `profile.js` still queried the removed standalone `#admin-link`, while the current hamburger menu uses `#auth-admin-link`. Updated the selector with an optional null guard, bumped the live asset to `profile.js?v=3`, rebuilt/verified the Docker service, and confirmed the profile route and full strict backend suite with 15 passing tests.
|
||||
|
||||
### User
|
||||
The post prefix is now 'From my #LinkLog: "' make that 'From my #LinkLog: '
|
||||
|
||||
### Assistant outcome
|
||||
Removed the trailing double quote from the default Mastodon post prefix in the publisher, profile UI, fallback, tests, and documentation. Removed the stale minified profile artifact and verified Mastodon/profile tests plus the full strict backend suite with 15 passing tests.
|
||||
|
||||
### User
|
||||
Use the logo.svg in both the plugin as well as on the web page
|
||||
|
||||
### Assistant outcome
|
||||
Bundled the supplied logo into the web static assets and Firefox extension, replaced text-only headers with accessible logo images, added responsive sizing, and verified all web pages, extension references, scripts, and the strict backend suite.
|
||||
|
||||
### User
|
||||
use the logo as the logo on the dropdown for firefox extensions but put it on a dark background
|
||||
|
||||
### Assistant outcome
|
||||
Updated the Firefox popup/dropdown with a dark Catppuccin-style background and matching dark form controls, tags fieldset, status states, and buttons. The existing bundled logo remains the popup brand mark and is readable against the dark surface. Firefox scripts and the strict backend suite passed.
|
||||
|
||||
### User
|
||||
Make sure that the logo is used in the toolbar
|
||||
|
||||
### Assistant outcome
|
||||
Configured the Firefox Manifest V3 action and extension icon declarations to use the bundled `logo.svg` at 16, 32, 48, and 96 pixel entries. Manifest validation, logo asset verification, extension syntax checks, and the strict backend suite passed.
|
||||
|
||||
### User
|
||||
For the logo in the toolbar make the background dark
|
||||
|
||||
### Assistant outcome
|
||||
Added a Catppuccin Mocha `#1e1e2e` background rectangle directly behind the Firefox toolbar logo artwork, preserving the foreground logo and source asset. SVG, manifest, extension scripts, and strict backend validation passed.
|
||||
|
||||
### User
|
||||
Make sure the addon conforms to Firefox guidelines
|
||||
|
||||
### Assistant outcome
|
||||
Reviewed the Firefox WebExtension package, retained permissions required by the active-tab capture flow, added stable Firefox-specific extension identity metadata, and generated exact square 16/32/48/96 PNG icons for toolbar and add-on metadata from the dark logo. Popup and settings branding continue to use `logo.svg`. Manifest paths, icon dimensions, extension scripts, and backend tests were validated successfully.
|
||||
|
||||
### User
|
||||
Create a make file that will regenerate the logo and icon files from LinkLog.svg
|
||||
|
||||
### Assistant outcome
|
||||
Added a root `Makefile` using `LinkLog.svg` as the source. `make logos` regenerates the web SVG, extension SVG, and square 16/32/48/96 PNG icons with ImageMagick. Added tool validation, generated-file cleanup, README instructions, and VIBE logging.
|
||||
|
||||
### User
|
||||
Make sure the addon conforms to Firefox guidelines
|
||||
|
||||
### Assistant outcome
|
||||
Validated the Firefox package and corrected its manifest structure. The toolbar and add-on metadata use exact square PNG icons, popup/settings use the SVG logo, stable Firefox-specific identity metadata is present, all referenced files exist, and extension scripts pass syntax validation. The backend suite remains green.
|
||||
|
||||
### User
|
||||
How come when te container starts after docker compose down -v the database is still populated with old links
|
||||
|
||||
### Assistant outcome
|
||||
Inspected the active container and confirmed it uses `/app/backend/data/linklog.db` from the Compose volume `linklog_linklog_data`, containing 230 links. The current Compose project maps logical `linklog_data` to that prefixed volume. `docker compose down` preserves it; only `docker compose down -v` from the same project removes it. A different directory/project, an external volume, or stopping without `-v` can therefore preserve the data.
|
||||
|
||||
### User
|
||||
Refine the Plugins Settings page. When the user is logged in it shows all "<user> logged in at <url>" and a sign-out button. If the user is not logged in it shows the form as is now.
|
||||
|
||||
### Assistant outcome
|
||||
Updated the Firefox extension settings page to restore valid stored sessions, show `<username> logged in at <backend URL>` with a sign-out button, and show the original login form when signed out or when token validation fails. Sign-out revokes the token when possible, clears stored credentials, and returns to the form. Extension and backend validation passed.
|
||||
|
||||
### User
|
||||
On the plugin, next to the logo, create a link of the form: [LinkLog](https://<host>/<user>)
|
||||
|
||||
### Assistant outcome
|
||||
Added a `LinkLog` link next to the Firefox popup logo. It uses the configured backend origin and authenticated username to target `/<user>/`, preserving HTTP or HTTPS from the configured backend. Popup HTML, scripts, manifest, and backend regression validation passed.
|
||||
|
||||
### User
|
||||
Use the Asset Font from google foundry as font for the LinkLog text
|
||||
|
||||
### Assistant outcome
|
||||
Applied the Google Foundry `Asset` font to visible LinkLog branding on the web and Firefox popup, retaining graceful local fallbacks for restricted or offline extension contexts. Font declarations, branding references, extension scripts, whitespace checks, and the strict backend suite passed.
|
||||
|
||||
### User
|
||||
Create an about page and add an entry in the hamburger menu. The about page explains what the linklog does
|
||||
|
||||
### Assistant outcome
|
||||
Added an `/about` page explaining LinkLog's link capture, cleanup, tagging, filtering, ownership, and Mastodon publishing workflow. Added an always-visible About item to every shared hamburger menu, documented the route, and verified all web routes, scripts, and the strict backend suite with 15 passing tests.
|
||||
|
||||
### User
|
||||
On the bottom of all files add a copyright statement (Olaf Kolkman) and a link to the git.kolkman.org/LinkLog repository in small print
|
||||
|
||||
### Assistant outcome
|
||||
Added small-print attribution footers to all seven web templates and both Firefox extension pages. Each footer contains `Copyright Olaf Kolkman` and a link to `https://git.kolkman.org/olaf/Link-Log`, with matching web and extension styling. All web routes, extension footer references, strict backend tests, and whitespace checks passed.
|
||||
|
||||
### User
|
||||
the link in the site footer must be underlined and in the same color as the other text
|
||||
|
||||
### Assistant outcome
|
||||
Updated the web footer repository link to remain the same muted color as the surrounding footer text and always display an underline. Added a footer rendering assertion and validated the site.
|
||||
|
||||
### User
|
||||
Add copyright sign and 2026 to the copyright notices
|
||||
|
||||
### Assistant outcome
|
||||
Updated all web and Firefox extension footer notices to `© 2026 Olaf Kolkman`, preserving the repository links and small-print styling. Verified all eight user-facing pages, whitespace, and the strict backend suite with 15 passing tests.
|
||||
|
||||
### User
|
||||
in the footer make the link the same color as the mother text, when clicked use a tint from the Catpuccin theme
|
||||
|
||||
### Assistant outcome
|
||||
Updated web and extension footer links to inherit the surrounding footer text color, remain underlined, and use Catppuccin lavender/mauve interaction tints for hover, focus, and active click states. Preserved the current repository URL edits and verified the strict backend suite with 15 passing tests.
|
||||
|
||||
### User
|
||||
Add a GNU License file
|
||||
|
||||
### Assistant outcome
|
||||
Added a root `LICENSE` file declaring GNU General Public License version 3 or any later version (GPL-3.0-or-later), with Olaf Kolkman copyright attribution and the official GNU license reference. Documented the license in `README.md`.
|
||||
|
||||
### User
|
||||
Add a copyright statement to source files refer to GNU license
|
||||
|
||||
### Assistant outcome
|
||||
Added `Copyright © 2026 Olaf Kolkman` and `SPDX-License-Identifier: GPL-3.0-or-later` headers to all 47 non-minified application, test, frontend, extension, build, deployment, and source-logo files. Generated/runtime assets were left untouched. Python and JavaScript compilation, Compose validation, whitespace checks, and the strict backend suite passed with 15 tests.
|
||||
|
||||
### User
|
||||
Add the version to the name of the resulting xpi
|
||||
|
||||
### Assistant outcome
|
||||
Updated the Makefile to read the extension version from `webextension/manifest.json` and produce a versioned archive such as `LinkLog-0.1.0.xpi`. Updated `.gitignore` and README documentation. The generated XPI contains 12 expected extension files and passes `unzip -tq` integrity validation.
|
||||
|
||||
### User
|
||||
Set the version number of the backend to 0.1.0
|
||||
|
||||
### Assistant outcome
|
||||
Added backend setting `version = 0.1.0`, wired it into FastAPI and OpenAPI metadata, and exposed `LINKLOG_VERSION` as an environment override. Added regression coverage and documented the setting. Compose validation and the strict backend suite passed with 15 tests.
|
||||
|
||||
### User
|
||||
Add the creation of an xpi bundle for the webextention to the Makefile
|
||||
|
||||
### Assistant outcome
|
||||
Added a portable `xpi` Makefile target that regenerates logo assets and packages the Firefox extension into `LinkLog.xpi`, excluding metadata and minified artifacts. Added README instructions, ignored the generated XPI, and verified the archive contents and integrity with `unzip -tq`.
|
||||
|
||||
@@ -65,6 +65,29 @@
|
||||
61. Make the tags filtering case incensitive (but maintain case of tags)
|
||||
62. Display the date as "2026 June 18 - 20:22"
|
||||
63. Create a page where users can add, delete, or edit labels - Labels can be edited and deleted by the same user that created them. The administrator can delete any label.
|
||||
64. The profile page shows in red: "can't access property \"classList\", document.querySelector(...) is null"
|
||||
65. The post prefix is now 'From my #LinkLog: "' make that 'From my #LinkLog: '
|
||||
66. Use the logo.svg in both the plugin as well as on the web page
|
||||
67. use the logo as the logo on the dropdown for firefox extensions but put it on a dark background
|
||||
68. Make sure that the logo is used in the toolbar
|
||||
69. For the logo in the toolbar make the background dark
|
||||
70. Make sure the addon conforms to Firefox guidelines
|
||||
71. Create a make file that will regenerate the logo and icon files from LinkLog.svg
|
||||
72. Make sure the addon conforms to Firefox guidelines
|
||||
73. How come when te container starts after docker compose down -v the database is still populated with old links
|
||||
74. Refine the Plugins Settings page. When the user is logged in it shows all "<user> logged in at <url>" and a sign-out button. If the user is not logged in it shows the form as is now.
|
||||
75. On the plugin, next to the logo, create a link of the form: [LinkLog](https://<host>/<user>)
|
||||
76. Use the Asset Font from google foundry as font for the LinkLog text
|
||||
77. Create an about page and add an entry in the hamburger menu. The about page explains what the linklog does
|
||||
78. On the bottom of all files add a copyright statement (Olaf Kolkman) and a link to the git.kolkman.org/LinkLog repository in small print
|
||||
79. the link in the site footer must be underlined and in the same color as the other text
|
||||
80. Add copyright sign and 2026 to the copyright notices
|
||||
81. in the footer make the link the same color as the mother text, when clicked use a tint from the Catpuccin theme
|
||||
82. Add a GNU License file
|
||||
83. Add a copyright statement to source files refer to GNU license
|
||||
84. Add the version to the name of the resulting xpi
|
||||
85. Set the version number of the backend to 0.1.0
|
||||
84. Add the creation of an xpi bundle for the webextention to the Makefile
|
||||
|
||||
## Future entries
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# VIBE
|
||||
|
||||
This code has mostly been vibe coded. For full transparency we maintain a log of prompts and results.
|
||||
|
||||
This folder contains an append-only record of the visible Link Log development conversation.
|
||||
|
||||
- `CHAT_LOG.md` records user requests and assistant responses or outcomes in chronological order.
|
||||
- `PROMPTS.md` records user prompts separately for quick reference.
|
||||
- [`CHAT_LOG.md`](./CHAT_LOG.md) records user requests and assistant responses or outcomes in chronological order.
|
||||
- [`PROMPTS.md`](PROMPTS.md) records user prompts separately for quick reference.
|
||||
|
||||
Only the user-visible conversation is recorded. System, developer, environment, and private tool instructions are intentionally excluded.
|
||||
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
"""LinkLog backend package."""
|
||||
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
@@ -1,3 +1,8 @@
|
||||
"""Legacy admin page wiring."""
|
||||
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.templating import Jinja2Templates
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import json
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from uuid import uuid4
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import APIRouter, Header, HTTPException, status
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import json
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from dataclasses import dataclass
|
||||
import os
|
||||
from pathlib import Path
|
||||
@@ -10,6 +13,7 @@ DB_PATH = BASE_DIR / 'data' / 'linklog.db'
|
||||
@dataclass
|
||||
class Settings:
|
||||
app_name: str = os.getenv('LINKLOG_APP_NAME', 'LinkLog')
|
||||
version: str = os.getenv('LINKLOG_VERSION', '0.1.0')
|
||||
database_url: str = os.getenv('LINKLOG_DATABASE_URL', f'sqlite:///{DB_PATH}')
|
||||
secret_key: str = os.getenv('LINKLOG_SECRET_KEY', 'dev-secret-key-change-me')
|
||||
token_expiry_days: int = int(os.getenv('LINKLOG_TOKEN_EXPIRY_DAYS', '30'))
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from hashlib import sha256
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
from hashlib import sha256
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.responses import HTMLResponse
|
||||
from fastapi.staticfiles import StaticFiles
|
||||
@@ -9,10 +12,11 @@ from backend.app.api.auth import router as auth_router
|
||||
from backend.app.api.links import router as links_router
|
||||
from backend.app.api.public import router as public_router
|
||||
from backend.app.api.user_config import router as user_config_router
|
||||
from backend.app.core.config import settings
|
||||
from backend.app.database import AVATARS_DIR
|
||||
from backend.app.services.link_service import list_public_links
|
||||
|
||||
app = FastAPI(title='LinkLog API')
|
||||
app = FastAPI(title='LinkLog API', version=settings.version)
|
||||
app.mount('/static', StaticFiles(directory='frontend/static'), name='static')
|
||||
app.mount('/media', StaticFiles(directory=AVATARS_DIR), name='media')
|
||||
app.include_router(auth_router, prefix='/api/auth')
|
||||
@@ -45,6 +49,11 @@ async def labels_page(request: Request):
|
||||
return templates.TemplateResponse(request, 'labels.html', {})
|
||||
|
||||
|
||||
@app.get('/about', response_class=HTMLResponse)
|
||||
async def about_page(request: Request):
|
||||
return templates.TemplateResponse(request, 'about.html', {})
|
||||
|
||||
|
||||
@app.get('/login', response_class=HTMLResponse)
|
||||
async def login_page(request: Request):
|
||||
return templates.TemplateResponse(request, 'login.html', {})
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
class BasePlugin:
|
||||
name = 'base'
|
||||
version = '1.0.0'
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from hashlib import sha256
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from uuid import uuid4
|
||||
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import json
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.request import Request, urlopen
|
||||
|
||||
from backend.app.plugins.base import BasePlugin
|
||||
|
||||
DEFAULT_POST_PREFIX = 'From my #LinkLog: "'
|
||||
DEFAULT_POST_PREFIX = 'From my #LinkLog: '
|
||||
|
||||
|
||||
class DefaultFrontendPlugin(BasePlugin):
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from hashlib import sha256
|
||||
from uuid import uuid4
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import json
|
||||
import threading
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
@@ -19,6 +22,7 @@ def login_headers(username='alice'):
|
||||
|
||||
|
||||
def test_login_returns_token():
|
||||
assert app.version == '0.1.0'
|
||||
response = client.post('/api/auth/login', json={
|
||||
'username': 'alice',
|
||||
'password': 'secret123',
|
||||
@@ -234,6 +238,9 @@ def test_logout_revokes_token_and_admin_can_list_plugins():
|
||||
def test_public_and_admin_pages_render_html():
|
||||
root_page = client.get('/')
|
||||
assert 'LinkLog' in root_page.text
|
||||
assert 'src="/static/logo.svg"' in root_page.text
|
||||
assert 'class="site-footer"' in root_page.text
|
||||
assert 'https://git.kolkman.org/' in root_page.text
|
||||
assert 'class="menu-toggle"' in root_page.text
|
||||
assert 'id="auth-menu" class="auth-menu hidden"' in root_page.text
|
||||
assert 'id="auth-home-link" href="/">Home</a>' in root_page.text
|
||||
@@ -255,8 +262,13 @@ def test_public_and_admin_pages_render_html():
|
||||
assert client.get('/login').status_code == 200
|
||||
login_page = client.get('/login').text
|
||||
assert 'Sign in' in login_page
|
||||
assert 'src="/static/logo.svg"' in login_page
|
||||
assert 'id="auth-session" class="auth-session hidden"' in login_page
|
||||
assert 'logout.js?v=3' in login_page
|
||||
about_page = client.get('/about')
|
||||
assert about_page.status_code == 200
|
||||
assert 'Save the good stuff' in about_page.text
|
||||
assert 'id="auth-about-link" href="/about"' in about_page.text
|
||||
assert client.get('/admin').status_code == 200
|
||||
admin_page = client.get('/admin').text
|
||||
assert 'Admin' in admin_page
|
||||
@@ -300,7 +312,7 @@ def test_link_submission_posts_to_enabled_mastodon_plugin():
|
||||
assert client.put('/api/user/plugins/mastodon', headers=headers, json={
|
||||
'instance': base_url,
|
||||
'access_token': 'test-token',
|
||||
'post_prefix': 'From my #LinkLog: "',
|
||||
'post_prefix': 'From my #LinkLog: ',
|
||||
}).status_code == 200
|
||||
assert client.put('/api/admin/plugins/mastodon', headers=headers, json={'enabled': True}).status_code == 200
|
||||
|
||||
@@ -315,7 +327,7 @@ def test_link_submission_posts_to_enabled_mastodon_plugin():
|
||||
assert received['path'] == '/api/v1/statuses'
|
||||
assert received['authorization'] == 'Bearer test-token'
|
||||
assert received['body'] == {
|
||||
'status': 'A useful page\nWorth sharing\nFrom my #LinkLog: "https://example.com/useful #python #web',
|
||||
'status': 'A useful page\nWorth sharing\nFrom my #LinkLog: https://example.com/useful #python #web',
|
||||
}
|
||||
finally:
|
||||
server.shutdown()
|
||||
@@ -328,12 +340,12 @@ def test_plugin_config_can_be_saved_for_mastodon():
|
||||
assert client.put('/api/user/plugins/mastodon', headers=headers, json={
|
||||
'instance': 'mastodon.social',
|
||||
'access_token': 'demo-token',
|
||||
'post_prefix': 'From my #LinkLog: "',
|
||||
'post_prefix': 'From my #LinkLog: ',
|
||||
}).status_code == 200
|
||||
|
||||
payload = client.get('/api/user/plugins/mastodon', headers=headers).json()
|
||||
assert payload['instance'] == 'mastodon.social'
|
||||
assert payload['post_prefix'] == 'From my #LinkLog: "'
|
||||
assert payload['post_prefix'] == 'From my #LinkLog: '
|
||||
|
||||
admin_update = client.put('/api/admin/plugins/mastodon', headers=headers, json={
|
||||
'enabled': True,
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
import sqlite3
|
||||
|
||||
from backend.app.database import DEFAULT_TAGS, apply_migrations, get_schema_version, seed_default_tags
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
## Copyright © 2026 Olaf Kolkman
|
||||
## SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from backend.app.main import app
|
||||
@@ -34,7 +37,7 @@ def test_user_config_api_and_profile_page():
|
||||
assert 'name="avatar" type="file"' in page_response.text
|
||||
assert 'name="avatar_url"' not in page_response.text
|
||||
assert 'value="mastodon.social"' in page_response.text
|
||||
assert 'From my #LinkLog: "' in page_response.text
|
||||
assert 'value="From my #LinkLog: "' in page_response.text
|
||||
assert 'id="auth-menu" class="auth-menu hidden"' in page_response.text
|
||||
assert 'id="auth-home-link" href="/">Home</a>' in page_response.text
|
||||
assert 'id="auth-profile-link" class="hidden"' in page_response.text
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
services:
|
||||
app:
|
||||
build:
|
||||
@@ -28,22 +31,6 @@ services:
|
||||
- "traefik.http.routers.linklog.entrypoints=web"
|
||||
- "traefik.http.services.linklog.loadbalancer.server.port=8000"
|
||||
|
||||
traefik:
|
||||
image: ${TRAEFIK_IMAGE:-traefik:v3.1}
|
||||
container_name: ${TRAEFIK_CONTAINER_NAME:-linklog-traefik}
|
||||
command:
|
||||
- --providers.docker=true
|
||||
- --entrypoints.web.address=:${TRAEFIK_HTTP_INTERNAL_PORT:-80}
|
||||
- --api.insecure=${TRAEFIK_API_INSECURE:-true}
|
||||
ports:
|
||||
- "${TRAEFIK_HTTP_PORT:-80}:${TRAEFIK_HTTP_INTERNAL_PORT:-80}"
|
||||
- "${TRAEFIK_DASHBOARD_BIND_ADDRESS:-127.0.0.1}:${TRAEFIK_DASHBOARD_PORT:-8080}:8080"
|
||||
restart: ${TRAEFIK_RESTART_POLICY:-unless-stopped}
|
||||
depends_on:
|
||||
app:
|
||||
condition: service_healthy
|
||||
volumes:
|
||||
- "${DOCKER_SOCKET_PATH:-/var/run/docker.sock}:/var/run/docker.sock:ro"
|
||||
|
||||
volumes:
|
||||
linklog_data:
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#!/bin/sh
|
||||
# Copyright © 2026 Olaf Kolkman
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
set -eu
|
||||
|
||||
chown -R linklog:linklog /app/backend/data
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
(() => {
|
||||
const pluginList = document.querySelector('#plugin-list');
|
||||
const adminLabelList = document.querySelector('#admin-label-list');
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
(() => {
|
||||
const loginButton = document.querySelector('#auth-login-button');
|
||||
const profileLink = document.querySelector('#auth-profile-link');
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
const feedEl = document.getElementById('feed');
|
||||
const sortSelect = document.getElementById('sort-select');
|
||||
const userFilter = document.getElementById('user-filter');
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
const form = document.querySelector('#login-form');
|
||||
const status = document.querySelector('#login-status');
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
||||
<g transform="matrix(0.972876,0,0,1.26057,-24.1588,-587.485)">
|
||||
<rect x="712.491" y="669.041" width="1854.11" height="1357.72" style="fill:rgb(88,91,112);"/>
|
||||
</g>
|
||||
<g transform="matrix(5.14628,0,0,6.12686,41.2191,-8673.98)">
|
||||
<path d="M141.95,1644.94L141.95,1633.94C157.554,1633.4 169.724,1631.04 178.462,1626.86C187.2,1622.69 193.344,1615.8 196.894,1606.21C200.444,1596.61 202.218,1583.47 202.218,1566.77C202.218,1554.44 201.146,1544.16 199,1535.93C196.855,1527.7 193.285,1521.25 188.292,1516.57C185.016,1513.6 181.154,1511.26 176.707,1509.54C172.26,1507.83 167.15,1506.62 161.376,1505.92C155.603,1505.21 149.128,1504.86 141.95,1504.86L141.95,1493.86L384.076,1493.86L384.076,1504.86C373.388,1504.86 364.221,1505.7 356.575,1507.38C348.929,1509.06 342.708,1512.12 337.909,1516.57C333.111,1521.01 329.581,1527.27 327.319,1535.35C325.056,1543.42 323.925,1553.9 323.925,1566.77L323.925,1633.36C339.06,1633.36 353.532,1631.08 367.341,1626.51C381.15,1621.95 393.399,1616 404.087,1608.66C414.776,1601.41 423.299,1593.3 429.657,1584.32C436.016,1575.35 439.546,1566.61 440.248,1558.11L452.536,1558.11C452.536,1562.4 452.419,1568.35 452.185,1575.96C451.951,1583.56 451.6,1591.85 451.132,1600.82C450.586,1609.95 449.942,1618.34 449.201,1625.99C448.46,1633.63 447.582,1639.95 446.568,1644.94L141.95,1644.94Z" style="fill:rgb(245,224,220);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.135097,0,0,0.266653,668.366,1282.68)">
|
||||
<path d="M846.315,1802.1L846.315,1747.02C926.615,1744.29 989.248,1732.47 1034.22,1711.57C1079.18,1690.67 1110.8,1656.2 1129.07,1608.15C1147.34,1560.11 1156.47,1494.29 1156.47,1410.69C1156.47,1348.97 1150.95,1297.51 1139.91,1256.3C1128.87,1215.09 1110.5,1182.76 1084.81,1159.33C1067.94,1144.48 1048.07,1132.76 1025.18,1124.17C1002.3,1115.58 975.999,1109.52 946.288,1106.01C916.577,1102.49 883.253,1100.73 846.315,1100.73L846.315,1045.65L2092.36,1045.65L2092.36,1100.73C2037.36,1100.73 1990.18,1104.93 1950.83,1113.33C1911.49,1121.73 1879.47,1137.06 1854.78,1159.33C1830.08,1181.59 1811.92,1212.94 1800.27,1253.37C1788.63,1293.8 1782.81,1346.24 1782.81,1410.69L1782.81,1744.09C1860.7,1744.09 1935.18,1732.66 2006.24,1709.81C2077.31,1686.96 2140.34,1657.18 2195.35,1620.46C2250.35,1584.13 2294.21,1543.51 2326.94,1498.58C2359.66,1453.66 2377.83,1409.91 2381.44,1367.33L2444.68,1367.33C2444.68,1388.82 2444.07,1418.6 2442.87,1456.69C2441.66,1494.78 2439.86,1536.28 2437.45,1581.2C2434.64,1626.9 2431.33,1668.9 2427.51,1707.18C2423.7,1745.46 2419.18,1777.1 2413.96,1802.1L846.315,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3043.37,1093.7C3001.22,1093.7 2961.37,1089.99 2923.83,1082.57C2886.29,1075.15 2853.66,1064.99 2825.96,1052.1C2797.86,1039.21 2775.67,1024.17 2759.41,1006.98C2743.15,989.794 2735.02,971.435 2735.02,951.904C2735.02,924.951 2748.97,900.732 2776.88,879.247C2804.78,857.763 2842.12,840.673 2888.9,827.978C2935.67,815.283 2987.16,808.935 3043.37,808.935C3101.59,808.935 3153.98,815.38 3200.56,828.271C3247.13,841.161 3284.07,858.447 3311.37,880.126C3338.67,901.806 3352.32,925.732 3352.32,951.904C3352.32,977.294 3338.67,1000.83 3311.37,1022.51C3284.07,1044.19 3247.13,1061.47 3200.56,1074.37C3153.98,1087.26 3101.59,1093.7 3043.37,1093.7ZM2498.94,1802.1L2498.94,1747.02C2608.95,1743.12 2686.24,1720.16 2730.81,1678.17C2775.37,1636.18 2797.66,1576.9 2797.66,1500.34C2797.66,1422.22 2776.08,1365.97 2732.91,1331.59C2689.75,1297.22 2618.39,1280.03 2518.82,1280.03L2518.82,1224.95L3327.03,1172.22L3327.03,1500.34C3327.03,1551.12 3333.76,1594.38 3347.2,1630.13C3360.66,1665.87 3386.05,1693.6 3423.39,1713.33C3460.73,1733.06 3515.53,1744.29 3587.8,1747.02L3587.8,1802.1L2498.94,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3625.19,1802.1L3625.19,1747.02C3724.76,1743.12 3796.43,1721.83 3840.19,1683.15C3862.68,1663.62 3878.94,1639.6 3888.97,1611.08C3899.01,1582.57 3904.03,1549.17 3904.03,1510.89C3904.03,1447.22 3894.7,1399.37 3876.03,1367.33C3857.36,1335.3 3827.64,1313.72 3786.89,1302.59C3746.14,1291.45 3692.24,1285.89 3625.19,1285.89L3625.19,1230.81L4433.4,1178.08L4433.4,1274.76C4505.67,1243.51 4581.56,1216.94 4661.05,1195.07C4740.55,1173.19 4822.65,1162.26 4907.37,1162.26C4940.29,1162.26 4973.62,1164.31 5007.34,1168.41C5041.07,1172.51 5073.19,1183.93 5103.7,1202.68C5122.98,1214.79 5139.84,1231.2 5154.29,1251.9C5168.34,1273 5179.59,1299.17 5188.02,1330.42C5196.45,1361.67 5200.66,1399.37 5200.66,1443.51L5199.46,1488.62C5198.26,1504.25 5197.65,1521.24 5197.65,1539.6C5197.65,1573.97 5199.46,1604.15 5203.07,1630.13C5206.69,1656.1 5215.92,1677.68 5230.78,1694.87C5245.23,1712.06 5267.51,1725.05 5297.63,1733.84C5327.74,1742.63 5369.29,1747.02 5422.29,1747.02L5422.29,1802.1L4575.53,1802.1L4575.53,1757.57C4601.63,1741.16 4619.7,1713.62 4629.74,1674.95C4639.77,1636.28 4644.79,1583.35 4644.79,1516.16C4644.79,1457.96 4639.47,1415.09 4628.83,1387.55C4618.19,1360.01 4603.74,1342.14 4585.47,1333.93C4567.2,1325.73 4546.83,1321.63 4524.34,1321.63C4510.29,1321.63 4495.54,1323.1 4480.08,1326.03C4464.62,1328.95 4449.06,1332.96 4433.4,1338.04L4433.4,1526.71C4433.4,1595.07 4438.52,1646.53 4448.76,1681.1C4459,1715.67 4476.97,1741.16 4502.66,1757.57L4502.66,1802.1L3625.19,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M6760.3,1835.5C6693.65,1835.5 6638.55,1833.06 6594.99,1828.17C6551.42,1823.29 6518.2,1814.4 6495.32,1801.51C6481.26,1793.7 6469.62,1783.35 6460.39,1770.46C6451.55,1757.96 6444.73,1743.31 6439.91,1726.51C6435.09,1709.72 6430.67,1690.38 6426.66,1668.51C6422.64,1647.41 6416.92,1627.68 6409.49,1609.33C6402.07,1590.97 6387.21,1575.83 6364.93,1563.91C6342.65,1552 6306.81,1545.85 6257.43,1545.46C6258.23,1615.38 6265.66,1666.45 6279.71,1698.68C6293.76,1730.91 6320.86,1747.02 6361.01,1747.02L6361.01,1802.1L5449.21,1802.1L5449.21,1747.02C5485.35,1745.85 5517.27,1742.92 5544.97,1738.23C5600.78,1728.86 5642.33,1707.96 5669.64,1675.54C5683.69,1658.35 5695.13,1636.87 5703.96,1611.08C5712.4,1585.3 5718.52,1554.15 5722.33,1517.63C5726.15,1481.1 5728.05,1438.43 5728.05,1389.6C5728.05,1315.38 5722.84,1255.22 5712.4,1209.13C5701.96,1163.04 5685.49,1127.1 5663.01,1101.32C5640.53,1075.93 5611.72,1058.74 5576.59,1049.76C5541.46,1040.77 5499,1036.28 5449.21,1036.28L5449.21,981.786L6257.43,929.052L6257.43,1462.84C6286.74,1442.92 6314.84,1422.12 6341.74,1400.44C6368.64,1378.76 6392.33,1358.54 6412.81,1339.79C6457.78,1298.39 6480.26,1272.8 6480.26,1263.04C6480.26,1255.22 6471.63,1250.15 6454.36,1247.8C6437.1,1245.46 6413.61,1244.29 6383.9,1244.29L6383.9,1189.21L7053.6,1189.21L7053.6,1244.29C7005.82,1244.29 6953.42,1249.76 6896.41,1260.69C6839.4,1271.63 6778.97,1288.04 6715.14,1309.91C6650.9,1331.79 6583.85,1359.33 6513.99,1392.53C6444.12,1425.73 6372.46,1464.6 6298.98,1509.13C6373.66,1497.41 6449.04,1488.13 6525.13,1481.3C6601.21,1474.46 6668.36,1471.04 6726.58,1471.04C6810.89,1471.04 6875.33,1480.42 6919.9,1499.17C6965.67,1518.7 6994.58,1550.34 7006.62,1594.09C7013.85,1619.87 7023.18,1641.94 7034.63,1660.3C7046.07,1678.66 7058.21,1693.9 7071.06,1706.01C7083.91,1718.12 7096.86,1727.2 7109.91,1733.25C7122.96,1739.31 7134.3,1742.92 7143.93,1744.09L7143.93,1805.62C7133.49,1808.74 7116.23,1811.96 7092.14,1815.28C7068.05,1818.6 7039.14,1821.83 7005.42,1824.95C6971.29,1828.08 6933.35,1830.62 6891.59,1832.57C6849.84,1834.52 6806.07,1835.5 6760.3,1835.5Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M7746.18,1802.1L7746.18,1747.02C7826.48,1744.29 7889.11,1732.47 7934.08,1711.57C7979.05,1690.67 8010.67,1656.2 8028.93,1608.15C8047.2,1560.11 8056.34,1494.29 8056.34,1410.69C8056.34,1348.97 8050.82,1297.51 8039.77,1256.3C8028.73,1215.09 8010.36,1182.76 7984.67,1159.33C7967.81,1144.48 7947.93,1132.76 7925.05,1124.17C7902.16,1115.58 7875.86,1109.52 7846.15,1106.01C7816.44,1102.49 7783.12,1100.73 7746.18,1100.73L7746.18,1045.65L8992.23,1045.65L8992.23,1100.73C8937.22,1100.73 8890.05,1104.93 8850.7,1113.33C8811.35,1121.73 8779.33,1137.06 8754.64,1159.33C8729.95,1181.59 8711.78,1212.94 8700.14,1253.37C8688.49,1293.8 8682.67,1346.24 8682.67,1410.69L8682.67,1744.09C8760.56,1744.09 8835.04,1732.66 8906.1,1709.81C8977.17,1686.96 9040.2,1657.18 9095.21,1620.46C9150.22,1584.13 9194.08,1543.51 9226.8,1498.58C9259.52,1453.66 9277.69,1409.91 9281.3,1367.33L9344.54,1367.33C9344.54,1388.82 9343.94,1418.6 9342.73,1456.69C9341.53,1494.78 9339.72,1536.28 9337.31,1581.2C9334.5,1626.9 9331.19,1668.9 9327.38,1707.18C9323.56,1745.46 9319.05,1777.1 9313.83,1802.1L7746.18,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M10190.6,1828.47C10046.1,1828.47 9919.52,1816.36 9810.91,1792.14C9702.31,1767.92 9617.89,1731.2 9557.67,1681.98C9497.44,1632.76 9467.33,1570.65 9467.33,1495.65C9467.33,1439.01 9484.69,1389.6 9519.43,1347.41C9554.15,1305.22 9603.24,1270.46 9666.67,1243.12C9730.11,1215.38 9806.19,1194.87 9894.93,1181.59C9983.66,1168.31 10082.2,1161.67 10190.6,1161.67C10298.6,1161.67 10397.2,1168.31 10486.3,1181.59C10575.5,1194.87 10651.5,1215.38 10714.6,1243.12C10778,1270.46 10827,1305.22 10861.5,1347.41C10896.1,1389.6 10913.3,1439.01 10913.3,1495.65C10913.3,1551.9 10896.1,1600.83 10861.5,1642.43C10827,1684.03 10778,1718.7 10714.6,1746.43C10651.5,1773.78 10575.5,1794.29 10486.3,1807.96C10397.2,1821.63 10298.6,1828.47 10190.6,1828.47ZM10190.6,1766.94C10209.5,1766.94 10226.5,1759.62 10241.5,1744.97C10256.6,1730.32 10269.5,1710.5 10280.4,1685.5C10302,1634.72 10312.9,1571.43 10312.9,1495.65C10312.9,1419.09 10302,1355.22 10280.4,1304.05C10269.5,1279.05 10256.6,1259.13 10241.5,1244.29C10226.5,1229.44 10209.5,1222.02 10190.6,1222.02C10171.4,1222.02 10154.2,1229.44 10139.1,1244.29C10124.1,1259.13 10111.3,1279.05 10100.9,1304.05C10090.1,1329.05 10081.8,1358.06 10076.2,1391.06C10070.6,1424.07 10067.8,1458.93 10067.8,1495.65C10067.8,1531.98 10070.6,1566.55 10076.2,1599.37C10081.8,1632.18 10090.1,1660.89 10100.9,1685.5C10111.3,1710.5 10124.1,1730.32 10139.1,1744.97C10154.2,1759.62 10171.4,1766.94 10190.6,1766.94Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M11680.5,2098C11591.8,2098 11505.8,2096.73 11422.7,2094.19C11339.6,2091.65 11261.9,2085.5 11189.7,2075.73C11166.4,2072.61 11144.8,2069.09 11124.9,2065.18C11105,2061.28 11086.5,2056.79 11069.2,2051.71C11035.1,2041.55 11008.3,2029.35 10988.8,2015.09C10969.3,2000.83 10959.6,1983.54 10959.6,1963.23C10959.6,1947.22 10967.4,1932.86 10983.1,1920.16C10998.7,1907.47 11020,1896.43 11046.9,1887.06C11073.4,1877.68 11104.1,1869.58 11139.1,1862.74C11174,1855.91 11210.7,1850.34 11249.3,1846.04C11216.4,1832.37 11189.8,1815.87 11169.5,1796.53C11149.2,1777.2 11139.1,1755.22 11139.1,1730.62C11139.1,1713.43 11144.3,1697.51 11154.7,1682.86C11165.2,1668.21 11179.4,1654.83 11197.5,1642.72C11233.2,1618.51 11279.6,1600.34 11336.6,1588.23C11278.4,1578.86 11227.2,1565.77 11183,1548.97C11138.9,1532.18 11104.2,1511.18 11079.1,1485.99C11054.1,1460.79 11041.5,1431.01 11041.5,1396.63C11041.5,1355.62 11059.5,1319.97 11095.4,1289.7C11131.3,1259.42 11179,1235.11 11238.4,1216.75C11297.5,1198.39 11365.3,1184.72 11442,1175.73C11518.7,1166.75 11599.2,1162.26 11683.5,1162.26C11753.4,1162.26 11820.9,1165.28 11886.2,1171.34C11951.4,1177.39 12011.7,1187.06 12067.1,1200.34C12080.8,1169.48 12099,1145.46 12121.9,1128.27C12144.8,1111.08 12170.6,1098.68 12199.3,1091.06C12228,1083.45 12257.7,1078.76 12288.5,1077C12319.2,1075.24 12349.2,1074.37 12378.5,1074.37C12397.4,1074.37 12418.1,1074.56 12440.8,1074.95C12463.5,1075.34 12488.3,1076.32 12515.2,1077.88C12512.8,1088.82 12510.6,1103.86 12508.6,1123C12506.6,1142.14 12501.5,1159.13 12493.5,1173.97C12484.7,1190.38 12467.6,1201.71 12442.3,1207.96C12417,1214.21 12393.1,1217.33 12370.7,1217.33L12230.9,1217.33C12215.7,1217.33 12200.9,1217.63 12186.7,1218.21C12172.4,1218.8 12159.1,1220.46 12146.6,1223.19C12200.4,1241.55 12243.7,1265.09 12276.4,1293.8C12309.1,1322.51 12325.5,1356.79 12325.5,1396.63C12325.5,1438.04 12307.6,1472.7 12271.9,1500.63C12236.2,1528.56 12188.6,1550.54 12129.2,1566.55C12069.3,1582.57 12001.4,1594.09 11925.3,1601.12C11849.2,1608.15 11768.6,1611.67 11683.5,1611.67C11638.5,1611.67 11594.4,1610.79 11551,1609.03C11507.6,1607.28 11465.7,1604.25 11425.1,1599.95L11425.7,1602.88C11413.7,1602.88 11402.2,1606.4 11391.4,1613.43C11380.6,1620.46 11375.1,1629.25 11375.1,1639.79C11375.1,1649.17 11380.5,1657.18 11391.1,1663.82C11401.7,1670.46 11415.7,1675.73 11433,1679.64C11450.6,1683.54 11470.8,1686.38 11493.5,1688.13C11516.2,1689.89 11540,1690.77 11564.9,1690.77L11884,1690.77C11957.1,1690.77 12026.9,1691.06 12093.3,1691.65C12159.8,1692.24 12222.1,1697.61 12280.3,1707.76C12334.1,1716.75 12377.9,1733.25 12411.6,1757.28C12445.3,1781.3 12462.2,1815.58 12462.2,1860.11C12462.2,1897.61 12450.8,1929.44 12427.9,1955.62C12405,1981.79 12373.6,2003.56 12333.6,2020.95C12293.7,2038.33 12247.8,2052.29 12196,2062.84C12145,2073 12090.1,2080.62 12031.3,2085.69C11972.5,2090.77 11913.1,2094.09 11853,2095.65C11793,2097.22 11735.5,2098 11680.5,2098ZM11682.9,1547.22C11712.2,1547.22 11735.9,1539.79 11754,1524.95C11772,1510.11 11785.2,1490.58 11793.4,1466.36C11801.6,1442.14 11805.8,1415.77 11805.8,1387.26C11805.8,1341.94 11796.3,1303.76 11777.4,1272.7C11758.6,1241.65 11727.1,1226.12 11682.9,1226.12C11639.1,1226.12 11607.9,1241.65 11589.2,1272.7C11570.6,1303.76 11561.2,1341.94 11561.2,1387.26C11561.2,1415.38 11565.4,1441.65 11573.6,1466.06C11581.8,1490.48 11594.9,1510.11 11612.7,1524.95C11630.6,1539.79 11654,1547.22 11682.9,1547.22ZM11708.2,2035.3C11747.1,2035.3 11786.1,2034.62 11825,2033.25C11864,2031.88 11899.5,2028.66 11931.6,2023.58C11964.1,2018.9 11990.8,2011.87 12011.7,2002.49C12032.6,1993.12 12043,1980.62 12043,1964.99C12043,1947.02 12029.1,1933.25 12001.2,1923.68C11973.3,1914.11 11935.4,1906.98 11887.7,1902.29C11839.5,1897.61 11782.4,1894.78 11716.3,1893.8C11650.3,1892.82 11579.1,1892.33 11502.8,1892.33C11490.8,1892.33 11478.6,1891.94 11466.4,1891.16C11454.1,1890.38 11442,1889.4 11430,1888.23C11404.3,1905.81 11391.4,1929.44 11391.4,1959.13C11391.4,1974.76 11398.9,1987.55 11414,1997.51C11429,2007.47 11450.4,2015.18 11478.1,2020.65C11505.8,2026.12 11539.2,2029.93 11578.1,2032.08C11617.1,2034.23 11660.4,2035.3 11708.2,2035.3Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
(() => {
|
||||
const logoutButton = document.querySelector('#logout-button');
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
(() => {
|
||||
const profileForm = document.querySelector('#profile-form');
|
||||
const passwordForm = document.querySelector('#password-form');
|
||||
@@ -5,7 +8,7 @@ const mastodonForm = document.querySelector('#mastodon-form');
|
||||
const profileLogoutButton = document.querySelector('#logout-button');
|
||||
const accessToken = localStorage.getItem('linklogAccessToken');
|
||||
const defaultMastodonInstance = 'mastodon.social';
|
||||
const defaultPostPrefix = 'From my #LinkLog: "';
|
||||
const defaultPostPrefix = 'From my #LinkLog: ';
|
||||
|
||||
function authHeaders(includeJson = false) {
|
||||
return {
|
||||
@@ -33,7 +36,7 @@ async function loadProfile() {
|
||||
avatarPreview.classList.remove('hidden');
|
||||
}
|
||||
if (profile.is_admin) {
|
||||
document.querySelector('#admin-link').classList.remove('hidden');
|
||||
document.querySelector('#auth-admin-link')?.classList.remove('hidden');
|
||||
}
|
||||
profileLogoutButton.classList.remove('hidden');
|
||||
}
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap');
|
||||
/* Copyright © 2026 Olaf Kolkman */
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Asset&family=DM+Sans:wght@400;500;600;700&family=Space+Grotesk:wght@500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--base: #1e1e2e;
|
||||
@@ -84,6 +87,20 @@ body::selection {
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
display: block;
|
||||
width: min(260px, 70vw);
|
||||
height: 150px;
|
||||
margin-bottom: 12px;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
.site-header .site-logo + h1,
|
||||
.feed-link {
|
||||
font-family: 'Asset', 'Space Grotesk', sans-serif;
|
||||
}
|
||||
|
||||
.site-header p {
|
||||
max-width: 34rem;
|
||||
margin: 14px 0 0;
|
||||
@@ -193,6 +210,26 @@ main.container {
|
||||
padding-bottom: 64px;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
max-width: 960px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px 24px;
|
||||
color: var(--muted);
|
||||
font-size: 0.72rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.site-footer a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.site-footer a:hover,
|
||||
.site-footer a:focus-visible,
|
||||
.site-footer a:active {
|
||||
color: var(--lavender);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
@@ -445,6 +482,11 @@ button:disabled {
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.about-content {
|
||||
display: grid;
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.link-item h2 {
|
||||
margin: 0 0 8px;
|
||||
color: var(--text);
|
||||
@@ -529,6 +571,11 @@ button:disabled {
|
||||
font-size: 2.35rem;
|
||||
}
|
||||
|
||||
.site-logo {
|
||||
width: min(220px, 68vw);
|
||||
height: 48px;
|
||||
}
|
||||
|
||||
.header-row {
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
@@ -555,6 +602,10 @@ button:disabled {
|
||||
padding-top: 18px;
|
||||
}
|
||||
|
||||
.site-footer {
|
||||
padding: 0 14px 18px;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
gap: 12px;
|
||||
padding: 14px;
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>About LinkLog</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" href="/static/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-header">
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<h1>About</h1>
|
||||
<p>A quiet place for the links worth keeping.</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
<a id="auth-admin-link" class="hidden" href="/admin">Admin</a>
|
||||
<div id="auth-session" class="auth-session hidden"><a id="auth-username" class="user-name" href="/"></a></div>
|
||||
<button id="logout-button" class="logout-button hidden" type="button">Sign out</button>
|
||||
</nav>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container about-content">
|
||||
<section class="link-item">
|
||||
<h2>Save the good stuff</h2>
|
||||
<p>LinkLog captures the page title, cleaned URL, comment, and timestamp when you save a page from the Firefox extension.</p>
|
||||
</section>
|
||||
<section class="link-item">
|
||||
<h2>Make it yours</h2>
|
||||
<p>Organize links with up to ten tags, browse the public feed, filter by user or tag, and edit the links you own.</p>
|
||||
</section>
|
||||
<section class="link-item">
|
||||
<h2>Share selectively</h2>
|
||||
<p>LinkLog stores the feed in SQLite and can publish new links to Mastodon when that plugin is configured.</p>
|
||||
</section>
|
||||
<section class="link-item">
|
||||
<h2>Open source</h2>
|
||||
<p>LinkLog is open source software. You can run your own instance, or contribute to the project on
|
||||
<a href="https://git.kolkman.org/olaf/Link-Log">my repository</a>.</p>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -11,13 +13,15 @@
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<h1>LinkLog Admin</h1>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<h1>Admin</h1>
|
||||
<p>Manage users and plugin configuration</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
@@ -70,6 +74,7 @@
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=2"></script>
|
||||
<script src="/static/admin.js?v=3"></script>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -11,13 +13,14 @@
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<h1>LinkLog</h1>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<p>Public link feed</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
@@ -72,6 +75,7 @@
|
||||
|
||||
<section id="feed" class="feed" aria-live="polite"></section>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=3"></script>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -11,6 +13,7 @@
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<h1>Labels</h1>
|
||||
<p>Manage your link labels</p>
|
||||
</div>
|
||||
@@ -18,6 +21,7 @@
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
@@ -40,6 +44,7 @@
|
||||
<div id="label-list" class="plugin-list"></div>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=3"></script>
|
||||
<script src="/static/labels.js?v=1"></script>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -11,13 +13,14 @@
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<div>
|
||||
<h1>Sign in</h1>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<p>Access your LinkLog settings</p>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
@@ -47,6 +50,7 @@
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=3"></script>
|
||||
<script src="/static/login.js"></script>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -10,11 +12,15 @@
|
||||
<header class="site-header">
|
||||
<div class="container">
|
||||
<div class="header-row">
|
||||
<h1>Profile</h1>
|
||||
<div>
|
||||
<img class="site-logo" src="/static/logo.svg" alt="LinkLog" />
|
||||
<h1>Profile</h1>
|
||||
</div>
|
||||
<div class="header-actions">
|
||||
<button class="menu-toggle" type="button" aria-expanded="false" aria-controls="auth-menu">Menu</button>
|
||||
<nav id="auth-menu" class="auth-menu hidden" aria-label="Account menu">
|
||||
<a id="auth-home-link" href="/">Home</a>
|
||||
<a id="auth-about-link" href="/about">About</a>
|
||||
<a id="auth-login-button" href="/login">Sign in</a>
|
||||
<a id="auth-profile-link" class="hidden" href="/profile">Profile</a>
|
||||
<a id="auth-labels-link" class="hidden" href="/labels">Labels</a>
|
||||
@@ -84,15 +90,16 @@
|
||||
</label>
|
||||
<label>
|
||||
Post prefix
|
||||
<input id="mastodon-post-prefix" name="post_prefix" type="text" value="From my #LinkLog: "" />
|
||||
<input id="mastodon-post-prefix" name="post_prefix" type="text" value="From my #LinkLog: " />
|
||||
</label>
|
||||
<button type="submit">Save Mastodon settings</button>
|
||||
<p id="mastodon-status" class="status" role="status"></p>
|
||||
</form>
|
||||
</section>
|
||||
</main>
|
||||
<footer class="site-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">git.kolkman.org/LinkLog</a></footer>
|
||||
<script src="/static/auth-header.js?v=3"></script>
|
||||
<script src="/static/logout.js?v=2"></script>
|
||||
<script src="/static/profile.js?v=2"></script>
|
||||
<script src="/static/profile.js?v=3"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"version": "0.1.0"
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate the version and checked-in artifacts for a LinkLog release."""
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[2]
|
||||
MANIFEST_PATH = ROOT / 'webextension' / 'manifest.json'
|
||||
FRONTEND_VERSION_PATH = ROOT / 'frontend' / 'version.json'
|
||||
SETTINGS_PATH = ROOT / 'backend' / 'app' / 'core' / 'config.py'
|
||||
UPDATES_PATH = ROOT / 'webextension' / 'updates.json'
|
||||
SIGNED_DIR = ROOT / 'XPI' / 'signed'
|
||||
|
||||
|
||||
def fail(message: str) -> None:
|
||||
raise SystemExit(f'release validation failed: {message}')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
manifest = json.loads(MANIFEST_PATH.read_text())
|
||||
extension_version = manifest.get('version')
|
||||
if not isinstance(extension_version, str) or not re.fullmatch(r'\d+\.\d+\.\d+', extension_version):
|
||||
fail('webextension/manifest.json has no valid three-part version')
|
||||
|
||||
frontend_version = json.loads(FRONTEND_VERSION_PATH.read_text()).get('version')
|
||||
if frontend_version != extension_version:
|
||||
fail(f'frontend version {frontend_version} does not match extension version {extension_version}')
|
||||
|
||||
gecko_settings = manifest.get('browser_specific_settings', {}).get('gecko', {})
|
||||
data_permissions = gecko_settings.get('data_collection_permissions')
|
||||
if data_permissions != {'required': ['websiteActivity'], 'optional': []}:
|
||||
fail('Firefox data_collection_permissions must require websiteActivity and have no optional categories')
|
||||
|
||||
settings = SETTINGS_PATH.read_text()
|
||||
match = re.search(r"version: str = os\.getenv\('LINKLOG_VERSION', '([^']+)'\)", settings)
|
||||
if not match:
|
||||
fail('backend version default could not be found')
|
||||
backend_version = match.group(1)
|
||||
if backend_version != extension_version:
|
||||
fail(f'backend version {backend_version} does not match extension version {extension_version}')
|
||||
|
||||
xpi_path = SIGNED_DIR / f'LinkLog-{extension_version}.xpi'
|
||||
if not xpi_path.is_file():
|
||||
fail(f'missing manually signed artifact: {xpi_path.relative_to(ROOT)}')
|
||||
with zipfile.ZipFile(xpi_path) as archive:
|
||||
try:
|
||||
packaged_manifest = json.loads(archive.read('manifest.json'))
|
||||
except KeyError:
|
||||
fail('signed XPI does not contain manifest.json')
|
||||
if packaged_manifest.get('version') != extension_version:
|
||||
fail('signed XPI manifest version does not match webextension/manifest.json')
|
||||
if archive.testzip() is not None:
|
||||
fail('signed XPI contains a corrupt member')
|
||||
|
||||
updates = json.loads(UPDATES_PATH.read_text())
|
||||
addon_id = gecko_settings['id']
|
||||
update_entries = updates.get('addons', {}).get(addon_id, {}).get('updates', [])
|
||||
if not any(entry.get('version') == extension_version for entry in update_entries):
|
||||
fail(f'webextension/updates.json has no update entry for {extension_version}')
|
||||
|
||||
print(f'validated LinkLog release {frontend_version}')
|
||||
print(f'xpi={xpi_path.relative_to(ROOT)}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Validate an unsigned LinkLog XPI produced by the Makefile."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
import zipfile
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
REQUIRED_FILES = {
|
||||
'manifest.json',
|
||||
'logo.svg',
|
||||
'icon-16.png',
|
||||
'icon-32.png',
|
||||
'icon-48.png',
|
||||
'icon-96.png',
|
||||
'options.css',
|
||||
'options.html',
|
||||
'options.js',
|
||||
'popup.css',
|
||||
'popup.html',
|
||||
'popup.js',
|
||||
}
|
||||
|
||||
|
||||
def fail(message: str) -> None:
|
||||
raise SystemExit(f'XPI validation failed: {message}')
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) != 3:
|
||||
fail('usage: validate_xpi.py <xpi> <source-manifest>')
|
||||
|
||||
xpi_path = Path(sys.argv[1])
|
||||
source_manifest_path = Path(sys.argv[2])
|
||||
try:
|
||||
source_manifest = json.loads(source_manifest_path.read_text())
|
||||
with zipfile.ZipFile(xpi_path) as archive:
|
||||
names = set(archive.namelist())
|
||||
corrupt_member = archive.testzip()
|
||||
if corrupt_member is not None:
|
||||
fail(f'corrupt archive member: {corrupt_member}')
|
||||
if 'manifest.json' not in names:
|
||||
fail('manifest.json is missing')
|
||||
packaged_manifest = json.loads(archive.read('manifest.json'))
|
||||
except (OSError, zipfile.BadZipFile, json.JSONDecodeError) as error:
|
||||
fail(str(error))
|
||||
|
||||
missing_files = REQUIRED_FILES - names
|
||||
if missing_files:
|
||||
fail(f'missing required files: {", ".join(sorted(missing_files))}')
|
||||
unexpected_files = names - REQUIRED_FILES
|
||||
if any(name.startswith('__MACOSX/') or name == '.DS_Store' for name in unexpected_files):
|
||||
fail('archive contains macOS metadata')
|
||||
if packaged_manifest != source_manifest:
|
||||
fail('packaged manifest does not match webextension/manifest.json')
|
||||
|
||||
print(f'validated unsigned XPI {xpi_path}')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
After Width: | Height: | Size: 950 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg width="100%" height="100%" viewBox="0 0 1804 1712" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;">
|
||||
<g transform="matrix(1,0,0,1,-669.006,-255.89)">
|
||||
<g transform="matrix(0.972876,0,0,1.26057,-24.1588,-587.485)">
|
||||
<rect x="712.491" y="669.041" width="1854.11" height="1357.72" style="fill:rgb(88,91,112);"/>
|
||||
</g>
|
||||
<g transform="matrix(5.14628,0,0,6.12686,41.2191,-8673.98)">
|
||||
<path d="M141.95,1644.94L141.95,1633.94C157.554,1633.4 169.724,1631.04 178.462,1626.86C187.2,1622.69 193.344,1615.8 196.894,1606.21C200.444,1596.61 202.218,1583.47 202.218,1566.77C202.218,1554.44 201.146,1544.16 199,1535.93C196.855,1527.7 193.285,1521.25 188.292,1516.57C185.016,1513.6 181.154,1511.26 176.707,1509.54C172.26,1507.83 167.15,1506.62 161.376,1505.92C155.603,1505.21 149.128,1504.86 141.95,1504.86L141.95,1493.86L384.076,1493.86L384.076,1504.86C373.388,1504.86 364.221,1505.7 356.575,1507.38C348.929,1509.06 342.708,1512.12 337.909,1516.57C333.111,1521.01 329.581,1527.27 327.319,1535.35C325.056,1543.42 323.925,1553.9 323.925,1566.77L323.925,1633.36C339.06,1633.36 353.532,1631.08 367.341,1626.51C381.15,1621.95 393.399,1616 404.087,1608.66C414.776,1601.41 423.299,1593.3 429.657,1584.32C436.016,1575.35 439.546,1566.61 440.248,1558.11L452.536,1558.11C452.536,1562.4 452.419,1568.35 452.185,1575.96C451.951,1583.56 451.6,1591.85 451.132,1600.82C450.586,1609.95 449.942,1618.34 449.201,1625.99C448.46,1633.63 447.582,1639.95 446.568,1644.94L141.95,1644.94Z" style="fill:rgb(245,224,220);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
<g transform="matrix(0.135097,0,0,0.266653,668.366,1282.68)">
|
||||
<path d="M846.315,1802.1L846.315,1747.02C926.615,1744.29 989.248,1732.47 1034.22,1711.57C1079.18,1690.67 1110.8,1656.2 1129.07,1608.15C1147.34,1560.11 1156.47,1494.29 1156.47,1410.69C1156.47,1348.97 1150.95,1297.51 1139.91,1256.3C1128.87,1215.09 1110.5,1182.76 1084.81,1159.33C1067.94,1144.48 1048.07,1132.76 1025.18,1124.17C1002.3,1115.58 975.999,1109.52 946.288,1106.01C916.577,1102.49 883.253,1100.73 846.315,1100.73L846.315,1045.65L2092.36,1045.65L2092.36,1100.73C2037.36,1100.73 1990.18,1104.93 1950.83,1113.33C1911.49,1121.73 1879.47,1137.06 1854.78,1159.33C1830.08,1181.59 1811.92,1212.94 1800.27,1253.37C1788.63,1293.8 1782.81,1346.24 1782.81,1410.69L1782.81,1744.09C1860.7,1744.09 1935.18,1732.66 2006.24,1709.81C2077.31,1686.96 2140.34,1657.18 2195.35,1620.46C2250.35,1584.13 2294.21,1543.51 2326.94,1498.58C2359.66,1453.66 2377.83,1409.91 2381.44,1367.33L2444.68,1367.33C2444.68,1388.82 2444.07,1418.6 2442.87,1456.69C2441.66,1494.78 2439.86,1536.28 2437.45,1581.2C2434.64,1626.9 2431.33,1668.9 2427.51,1707.18C2423.7,1745.46 2419.18,1777.1 2413.96,1802.1L846.315,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3043.37,1093.7C3001.22,1093.7 2961.37,1089.99 2923.83,1082.57C2886.29,1075.15 2853.66,1064.99 2825.96,1052.1C2797.86,1039.21 2775.67,1024.17 2759.41,1006.98C2743.15,989.794 2735.02,971.435 2735.02,951.904C2735.02,924.951 2748.97,900.732 2776.88,879.247C2804.78,857.763 2842.12,840.673 2888.9,827.978C2935.67,815.283 2987.16,808.935 3043.37,808.935C3101.59,808.935 3153.98,815.38 3200.56,828.271C3247.13,841.161 3284.07,858.447 3311.37,880.126C3338.67,901.806 3352.32,925.732 3352.32,951.904C3352.32,977.294 3338.67,1000.83 3311.37,1022.51C3284.07,1044.19 3247.13,1061.47 3200.56,1074.37C3153.98,1087.26 3101.59,1093.7 3043.37,1093.7ZM2498.94,1802.1L2498.94,1747.02C2608.95,1743.12 2686.24,1720.16 2730.81,1678.17C2775.37,1636.18 2797.66,1576.9 2797.66,1500.34C2797.66,1422.22 2776.08,1365.97 2732.91,1331.59C2689.75,1297.22 2618.39,1280.03 2518.82,1280.03L2518.82,1224.95L3327.03,1172.22L3327.03,1500.34C3327.03,1551.12 3333.76,1594.38 3347.2,1630.13C3360.66,1665.87 3386.05,1693.6 3423.39,1713.33C3460.73,1733.06 3515.53,1744.29 3587.8,1747.02L3587.8,1802.1L2498.94,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M3625.19,1802.1L3625.19,1747.02C3724.76,1743.12 3796.43,1721.83 3840.19,1683.15C3862.68,1663.62 3878.94,1639.6 3888.97,1611.08C3899.01,1582.57 3904.03,1549.17 3904.03,1510.89C3904.03,1447.22 3894.7,1399.37 3876.03,1367.33C3857.36,1335.3 3827.64,1313.72 3786.89,1302.59C3746.14,1291.45 3692.24,1285.89 3625.19,1285.89L3625.19,1230.81L4433.4,1178.08L4433.4,1274.76C4505.67,1243.51 4581.56,1216.94 4661.05,1195.07C4740.55,1173.19 4822.65,1162.26 4907.37,1162.26C4940.29,1162.26 4973.62,1164.31 5007.34,1168.41C5041.07,1172.51 5073.19,1183.93 5103.7,1202.68C5122.98,1214.79 5139.84,1231.2 5154.29,1251.9C5168.34,1273 5179.59,1299.17 5188.02,1330.42C5196.45,1361.67 5200.66,1399.37 5200.66,1443.51L5199.46,1488.62C5198.26,1504.25 5197.65,1521.24 5197.65,1539.6C5197.65,1573.97 5199.46,1604.15 5203.07,1630.13C5206.69,1656.1 5215.92,1677.68 5230.78,1694.87C5245.23,1712.06 5267.51,1725.05 5297.63,1733.84C5327.74,1742.63 5369.29,1747.02 5422.29,1747.02L5422.29,1802.1L4575.53,1802.1L4575.53,1757.57C4601.63,1741.16 4619.7,1713.62 4629.74,1674.95C4639.77,1636.28 4644.79,1583.35 4644.79,1516.16C4644.79,1457.96 4639.47,1415.09 4628.83,1387.55C4618.19,1360.01 4603.74,1342.14 4585.47,1333.93C4567.2,1325.73 4546.83,1321.63 4524.34,1321.63C4510.29,1321.63 4495.54,1323.1 4480.08,1326.03C4464.62,1328.95 4449.06,1332.96 4433.4,1338.04L4433.4,1526.71C4433.4,1595.07 4438.52,1646.53 4448.76,1681.1C4459,1715.67 4476.97,1741.16 4502.66,1757.57L4502.66,1802.1L3625.19,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M6760.3,1835.5C6693.65,1835.5 6638.55,1833.06 6594.99,1828.17C6551.42,1823.29 6518.2,1814.4 6495.32,1801.51C6481.26,1793.7 6469.62,1783.35 6460.39,1770.46C6451.55,1757.96 6444.73,1743.31 6439.91,1726.51C6435.09,1709.72 6430.67,1690.38 6426.66,1668.51C6422.64,1647.41 6416.92,1627.68 6409.49,1609.33C6402.07,1590.97 6387.21,1575.83 6364.93,1563.91C6342.65,1552 6306.81,1545.85 6257.43,1545.46C6258.23,1615.38 6265.66,1666.45 6279.71,1698.68C6293.76,1730.91 6320.86,1747.02 6361.01,1747.02L6361.01,1802.1L5449.21,1802.1L5449.21,1747.02C5485.35,1745.85 5517.27,1742.92 5544.97,1738.23C5600.78,1728.86 5642.33,1707.96 5669.64,1675.54C5683.69,1658.35 5695.13,1636.87 5703.96,1611.08C5712.4,1585.3 5718.52,1554.15 5722.33,1517.63C5726.15,1481.1 5728.05,1438.43 5728.05,1389.6C5728.05,1315.38 5722.84,1255.22 5712.4,1209.13C5701.96,1163.04 5685.49,1127.1 5663.01,1101.32C5640.53,1075.93 5611.72,1058.74 5576.59,1049.76C5541.46,1040.77 5499,1036.28 5449.21,1036.28L5449.21,981.786L6257.43,929.052L6257.43,1462.84C6286.74,1442.92 6314.84,1422.12 6341.74,1400.44C6368.64,1378.76 6392.33,1358.54 6412.81,1339.79C6457.78,1298.39 6480.26,1272.8 6480.26,1263.04C6480.26,1255.22 6471.63,1250.15 6454.36,1247.8C6437.1,1245.46 6413.61,1244.29 6383.9,1244.29L6383.9,1189.21L7053.6,1189.21L7053.6,1244.29C7005.82,1244.29 6953.42,1249.76 6896.41,1260.69C6839.4,1271.63 6778.97,1288.04 6715.14,1309.91C6650.9,1331.79 6583.85,1359.33 6513.99,1392.53C6444.12,1425.73 6372.46,1464.6 6298.98,1509.13C6373.66,1497.41 6449.04,1488.13 6525.13,1481.3C6601.21,1474.46 6668.36,1471.04 6726.58,1471.04C6810.89,1471.04 6875.33,1480.42 6919.9,1499.17C6965.67,1518.7 6994.58,1550.34 7006.62,1594.09C7013.85,1619.87 7023.18,1641.94 7034.63,1660.3C7046.07,1678.66 7058.21,1693.9 7071.06,1706.01C7083.91,1718.12 7096.86,1727.2 7109.91,1733.25C7122.96,1739.31 7134.3,1742.92 7143.93,1744.09L7143.93,1805.62C7133.49,1808.74 7116.23,1811.96 7092.14,1815.28C7068.05,1818.6 7039.14,1821.83 7005.42,1824.95C6971.29,1828.08 6933.35,1830.62 6891.59,1832.57C6849.84,1834.52 6806.07,1835.5 6760.3,1835.5Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M7746.18,1802.1L7746.18,1747.02C7826.48,1744.29 7889.11,1732.47 7934.08,1711.57C7979.05,1690.67 8010.67,1656.2 8028.93,1608.15C8047.2,1560.11 8056.34,1494.29 8056.34,1410.69C8056.34,1348.97 8050.82,1297.51 8039.77,1256.3C8028.73,1215.09 8010.36,1182.76 7984.67,1159.33C7967.81,1144.48 7947.93,1132.76 7925.05,1124.17C7902.16,1115.58 7875.86,1109.52 7846.15,1106.01C7816.44,1102.49 7783.12,1100.73 7746.18,1100.73L7746.18,1045.65L8992.23,1045.65L8992.23,1100.73C8937.22,1100.73 8890.05,1104.93 8850.7,1113.33C8811.35,1121.73 8779.33,1137.06 8754.64,1159.33C8729.95,1181.59 8711.78,1212.94 8700.14,1253.37C8688.49,1293.8 8682.67,1346.24 8682.67,1410.69L8682.67,1744.09C8760.56,1744.09 8835.04,1732.66 8906.1,1709.81C8977.17,1686.96 9040.2,1657.18 9095.21,1620.46C9150.22,1584.13 9194.08,1543.51 9226.8,1498.58C9259.52,1453.66 9277.69,1409.91 9281.3,1367.33L9344.54,1367.33C9344.54,1388.82 9343.94,1418.6 9342.73,1456.69C9341.53,1494.78 9339.72,1536.28 9337.31,1581.2C9334.5,1626.9 9331.19,1668.9 9327.38,1707.18C9323.56,1745.46 9319.05,1777.1 9313.83,1802.1L7746.18,1802.1Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M10190.6,1828.47C10046.1,1828.47 9919.52,1816.36 9810.91,1792.14C9702.31,1767.92 9617.89,1731.2 9557.67,1681.98C9497.44,1632.76 9467.33,1570.65 9467.33,1495.65C9467.33,1439.01 9484.69,1389.6 9519.43,1347.41C9554.15,1305.22 9603.24,1270.46 9666.67,1243.12C9730.11,1215.38 9806.19,1194.87 9894.93,1181.59C9983.66,1168.31 10082.2,1161.67 10190.6,1161.67C10298.6,1161.67 10397.2,1168.31 10486.3,1181.59C10575.5,1194.87 10651.5,1215.38 10714.6,1243.12C10778,1270.46 10827,1305.22 10861.5,1347.41C10896.1,1389.6 10913.3,1439.01 10913.3,1495.65C10913.3,1551.9 10896.1,1600.83 10861.5,1642.43C10827,1684.03 10778,1718.7 10714.6,1746.43C10651.5,1773.78 10575.5,1794.29 10486.3,1807.96C10397.2,1821.63 10298.6,1828.47 10190.6,1828.47ZM10190.6,1766.94C10209.5,1766.94 10226.5,1759.62 10241.5,1744.97C10256.6,1730.32 10269.5,1710.5 10280.4,1685.5C10302,1634.72 10312.9,1571.43 10312.9,1495.65C10312.9,1419.09 10302,1355.22 10280.4,1304.05C10269.5,1279.05 10256.6,1259.13 10241.5,1244.29C10226.5,1229.44 10209.5,1222.02 10190.6,1222.02C10171.4,1222.02 10154.2,1229.44 10139.1,1244.29C10124.1,1259.13 10111.3,1279.05 10100.9,1304.05C10090.1,1329.05 10081.8,1358.06 10076.2,1391.06C10070.6,1424.07 10067.8,1458.93 10067.8,1495.65C10067.8,1531.98 10070.6,1566.55 10076.2,1599.37C10081.8,1632.18 10090.1,1660.89 10100.9,1685.5C10111.3,1710.5 10124.1,1730.32 10139.1,1744.97C10154.2,1759.62 10171.4,1766.94 10190.6,1766.94Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
<path d="M11680.5,2098C11591.8,2098 11505.8,2096.73 11422.7,2094.19C11339.6,2091.65 11261.9,2085.5 11189.7,2075.73C11166.4,2072.61 11144.8,2069.09 11124.9,2065.18C11105,2061.28 11086.5,2056.79 11069.2,2051.71C11035.1,2041.55 11008.3,2029.35 10988.8,2015.09C10969.3,2000.83 10959.6,1983.54 10959.6,1963.23C10959.6,1947.22 10967.4,1932.86 10983.1,1920.16C10998.7,1907.47 11020,1896.43 11046.9,1887.06C11073.4,1877.68 11104.1,1869.58 11139.1,1862.74C11174,1855.91 11210.7,1850.34 11249.3,1846.04C11216.4,1832.37 11189.8,1815.87 11169.5,1796.53C11149.2,1777.2 11139.1,1755.22 11139.1,1730.62C11139.1,1713.43 11144.3,1697.51 11154.7,1682.86C11165.2,1668.21 11179.4,1654.83 11197.5,1642.72C11233.2,1618.51 11279.6,1600.34 11336.6,1588.23C11278.4,1578.86 11227.2,1565.77 11183,1548.97C11138.9,1532.18 11104.2,1511.18 11079.1,1485.99C11054.1,1460.79 11041.5,1431.01 11041.5,1396.63C11041.5,1355.62 11059.5,1319.97 11095.4,1289.7C11131.3,1259.42 11179,1235.11 11238.4,1216.75C11297.5,1198.39 11365.3,1184.72 11442,1175.73C11518.7,1166.75 11599.2,1162.26 11683.5,1162.26C11753.4,1162.26 11820.9,1165.28 11886.2,1171.34C11951.4,1177.39 12011.7,1187.06 12067.1,1200.34C12080.8,1169.48 12099,1145.46 12121.9,1128.27C12144.8,1111.08 12170.6,1098.68 12199.3,1091.06C12228,1083.45 12257.7,1078.76 12288.5,1077C12319.2,1075.24 12349.2,1074.37 12378.5,1074.37C12397.4,1074.37 12418.1,1074.56 12440.8,1074.95C12463.5,1075.34 12488.3,1076.32 12515.2,1077.88C12512.8,1088.82 12510.6,1103.86 12508.6,1123C12506.6,1142.14 12501.5,1159.13 12493.5,1173.97C12484.7,1190.38 12467.6,1201.71 12442.3,1207.96C12417,1214.21 12393.1,1217.33 12370.7,1217.33L12230.9,1217.33C12215.7,1217.33 12200.9,1217.63 12186.7,1218.21C12172.4,1218.8 12159.1,1220.46 12146.6,1223.19C12200.4,1241.55 12243.7,1265.09 12276.4,1293.8C12309.1,1322.51 12325.5,1356.79 12325.5,1396.63C12325.5,1438.04 12307.6,1472.7 12271.9,1500.63C12236.2,1528.56 12188.6,1550.54 12129.2,1566.55C12069.3,1582.57 12001.4,1594.09 11925.3,1601.12C11849.2,1608.15 11768.6,1611.67 11683.5,1611.67C11638.5,1611.67 11594.4,1610.79 11551,1609.03C11507.6,1607.28 11465.7,1604.25 11425.1,1599.95L11425.7,1602.88C11413.7,1602.88 11402.2,1606.4 11391.4,1613.43C11380.6,1620.46 11375.1,1629.25 11375.1,1639.79C11375.1,1649.17 11380.5,1657.18 11391.1,1663.82C11401.7,1670.46 11415.7,1675.73 11433,1679.64C11450.6,1683.54 11470.8,1686.38 11493.5,1688.13C11516.2,1689.89 11540,1690.77 11564.9,1690.77L11884,1690.77C11957.1,1690.77 12026.9,1691.06 12093.3,1691.65C12159.8,1692.24 12222.1,1697.61 12280.3,1707.76C12334.1,1716.75 12377.9,1733.25 12411.6,1757.28C12445.3,1781.3 12462.2,1815.58 12462.2,1860.11C12462.2,1897.61 12450.8,1929.44 12427.9,1955.62C12405,1981.79 12373.6,2003.56 12333.6,2020.95C12293.7,2038.33 12247.8,2052.29 12196,2062.84C12145,2073 12090.1,2080.62 12031.3,2085.69C11972.5,2090.77 11913.1,2094.09 11853,2095.65C11793,2097.22 11735.5,2098 11680.5,2098ZM11682.9,1547.22C11712.2,1547.22 11735.9,1539.79 11754,1524.95C11772,1510.11 11785.2,1490.58 11793.4,1466.36C11801.6,1442.14 11805.8,1415.77 11805.8,1387.26C11805.8,1341.94 11796.3,1303.76 11777.4,1272.7C11758.6,1241.65 11727.1,1226.12 11682.9,1226.12C11639.1,1226.12 11607.9,1241.65 11589.2,1272.7C11570.6,1303.76 11561.2,1341.94 11561.2,1387.26C11561.2,1415.38 11565.4,1441.65 11573.6,1466.06C11581.8,1490.48 11594.9,1510.11 11612.7,1524.95C11630.6,1539.79 11654,1547.22 11682.9,1547.22ZM11708.2,2035.3C11747.1,2035.3 11786.1,2034.62 11825,2033.25C11864,2031.88 11899.5,2028.66 11931.6,2023.58C11964.1,2018.9 11990.8,2011.87 12011.7,2002.49C12032.6,1993.12 12043,1980.62 12043,1964.99C12043,1947.02 12029.1,1933.25 12001.2,1923.68C11973.3,1914.11 11935.4,1906.98 11887.7,1902.29C11839.5,1897.61 11782.4,1894.78 11716.3,1893.8C11650.3,1892.82 11579.1,1892.33 11502.8,1892.33C11490.8,1892.33 11478.6,1891.94 11466.4,1891.16C11454.1,1890.38 11442,1889.4 11430,1888.23C11404.3,1905.81 11391.4,1929.44 11391.4,1959.13C11391.4,1974.76 11398.9,1987.55 11414,1997.51C11429,2007.47 11450.4,2015.18 11478.1,2020.65C11505.8,2026.12 11539.2,2029.93 11578.1,2032.08C11617.1,2034.23 11660.4,2035.3 11708.2,2035.3Z" style="fill:rgb(203,166,247);fill-rule:nonzero;"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 14 KiB |
@@ -13,7 +13,26 @@
|
||||
],
|
||||
"action": {
|
||||
"default_title": "LinkLog",
|
||||
"default_popup": "popup.html"
|
||||
"default_popup": "popup.html",
|
||||
"default_icon": {
|
||||
"16": "icon-16.png",
|
||||
"32": "icon-32.png"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"48": "icon-48.png",
|
||||
"96": "icon-96.png"
|
||||
},
|
||||
"browser_specific_settings": {
|
||||
"gecko": {
|
||||
"id": "linklog@kolkman.org",
|
||||
"strict_min_version": "109.0",
|
||||
"data_collection_permissions": {
|
||||
"required": ["websiteActivity"],
|
||||
"optional": []
|
||||
},
|
||||
"update_url": "https://git.kolkman.org/olaf/Link-Log/raw/branch/main/webextension/updates.json"
|
||||
}
|
||||
},
|
||||
"options_ui": {
|
||||
"page": "options.html",
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
/* Copyright © 2026 Olaf Kolkman */
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
@@ -18,6 +21,15 @@ h1 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.extension-logo {
|
||||
display: block;
|
||||
width: 190px;
|
||||
height: 52px;
|
||||
margin-bottom: 8px;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
@@ -66,6 +78,43 @@ button {
|
||||
color: #991b1b;
|
||||
}
|
||||
|
||||
.logged-in {
|
||||
margin-bottom: 16px;
|
||||
padding: 14px;
|
||||
border: 1px solid #45475a;
|
||||
border-radius: 8px;
|
||||
background: #313244;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
.logged-in p {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.logged-in button {
|
||||
background: #f38ba8;
|
||||
color: #11111b;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.extension-footer {
|
||||
margin: 16px auto;
|
||||
color: #7f849c;
|
||||
font-size: 0.68rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extension-footer a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.extension-footer a:hover,
|
||||
.extension-footer a:focus-visible,
|
||||
.extension-footer a:active {
|
||||
color: #b4befe;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -7,10 +9,16 @@
|
||||
</head>
|
||||
<body>
|
||||
<main class="options-shell">
|
||||
<h1>LinkLog Settings</h1>
|
||||
<img class="extension-logo" src="logo.svg" alt="LinkLog" />
|
||||
<h1>Settings</h1>
|
||||
|
||||
<div id="status" class="status hidden" aria-live="polite"></div>
|
||||
|
||||
<section id="logged-in" class="logged-in hidden" aria-live="polite">
|
||||
<p id="session-summary"></p>
|
||||
<button type="button" id="sign-out">Sign out</button>
|
||||
</section>
|
||||
|
||||
<form id="settings-form">
|
||||
<label>
|
||||
Backend URL
|
||||
@@ -30,6 +38,7 @@
|
||||
<button type="submit">Save and log in</button>
|
||||
</form>
|
||||
</main>
|
||||
<footer class="extension-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">Repository</a></footer>
|
||||
|
||||
<script src="options.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
const DEFAULT_BACKEND = 'http://localhost:8000';
|
||||
|
||||
const statusEl = document.getElementById('status');
|
||||
@@ -5,6 +8,9 @@ const form = document.getElementById('settings-form');
|
||||
const backendUrlInput = document.getElementById('backend-url');
|
||||
const usernameInput = document.getElementById('username');
|
||||
const passwordInput = document.getElementById('password');
|
||||
const session = document.getElementById('logged-in');
|
||||
const sessionSummary = document.getElementById('session-summary');
|
||||
const signOutButton = document.getElementById('sign-out');
|
||||
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message;
|
||||
@@ -14,9 +20,41 @@ function setStatus(message, isError = false) {
|
||||
}
|
||||
|
||||
async function loadSettings() {
|
||||
const settings = await browser.storage.local.get(['backendUrl', 'username']);
|
||||
const settings = await browser.storage.local.get(['backendUrl', 'username', 'accessToken']);
|
||||
backendUrlInput.value = settings.backendUrl || DEFAULT_BACKEND;
|
||||
usernameInput.value = settings.username || '';
|
||||
|
||||
if (settings.accessToken && settings.backendUrl) {
|
||||
try {
|
||||
const response = await fetch(
|
||||
`${settings.backendUrl}/api/auth/me?token=${encodeURIComponent(settings.accessToken)}`
|
||||
);
|
||||
if (response.ok) {
|
||||
const user = await response.json();
|
||||
showLoggedIn(user.username || settings.username, settings.backendUrl);
|
||||
return;
|
||||
}
|
||||
} catch (error) {
|
||||
// Show the login form when the backend cannot validate the stored token.
|
||||
}
|
||||
await clearSession();
|
||||
}
|
||||
showLoggedOut();
|
||||
}
|
||||
|
||||
function showLoggedIn(username, backendUrl) {
|
||||
sessionSummary.textContent = `${username} logged in at ${backendUrl}`;
|
||||
session.classList.remove('hidden');
|
||||
form.classList.add('hidden');
|
||||
}
|
||||
|
||||
function showLoggedOut() {
|
||||
session.classList.add('hidden');
|
||||
form.classList.remove('hidden');
|
||||
}
|
||||
|
||||
async function clearSession() {
|
||||
await browser.storage.local.remove(['accessToken', 'tokenType', 'tokenExpiresAt', 'refreshToken']);
|
||||
}
|
||||
|
||||
async function saveSettingsAndLogin(event) {
|
||||
@@ -51,11 +89,28 @@ async function saveSettingsAndLogin(event) {
|
||||
refreshToken: data.refresh_token,
|
||||
});
|
||||
|
||||
showLoggedIn(data.user?.username || username, backendUrl);
|
||||
passwordInput.value = '';
|
||||
setStatus('Logged in successfully');
|
||||
} catch (error) {
|
||||
setStatus('Unable to log in. Check backend URL and credentials.', true);
|
||||
}
|
||||
}
|
||||
|
||||
async function signOut() {
|
||||
const settings = await browser.storage.local.get(['accessToken']);
|
||||
if (settings.accessToken) {
|
||||
await fetch(`${backendUrlInput.value.trim()}/api/auth/logout`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ token: settings.accessToken }),
|
||||
}).catch(() => undefined);
|
||||
}
|
||||
await clearSession();
|
||||
showLoggedOut();
|
||||
setStatus('Signed out');
|
||||
}
|
||||
|
||||
form.addEventListener('submit', saveSettingsAndLogin);
|
||||
signOutButton.addEventListener('click', signOut);
|
||||
loadSettings();
|
||||
|
||||
@@ -1,13 +1,17 @@
|
||||
/* Copyright © 2026 Olaf Kolkman */
|
||||
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
||||
|
||||
body {
|
||||
width: 360px;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
background: #f5f7fb;
|
||||
color: #1f2937;
|
||||
font-family: 'Avenir Next', sans-serif;
|
||||
background: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
.popup-shell {
|
||||
padding: 16px;
|
||||
background: linear-gradient(145deg, #313244, #1e1e2e 42%);
|
||||
}
|
||||
|
||||
header h1 {
|
||||
@@ -15,11 +19,41 @@ header h1 {
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.extension-logo {
|
||||
display: block;
|
||||
width: 150px;
|
||||
height: 42px;
|
||||
margin-bottom: 0;
|
||||
object-fit: contain;
|
||||
object-position: left center;
|
||||
}
|
||||
|
||||
.feed-link {
|
||||
color: #b4befe;
|
||||
font-family: 'Asset', 'Avenir Next', sans-serif;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.feed-link:hover {
|
||||
color: #cba6f7;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
label {
|
||||
display: block;
|
||||
margin-bottom: 12px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
color: #a6adc8;
|
||||
}
|
||||
|
||||
input,
|
||||
@@ -33,10 +67,11 @@ button {
|
||||
input,
|
||||
textarea {
|
||||
margin-top: 6px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 8px;
|
||||
padding: 8px 10px;
|
||||
background: white;
|
||||
background: #313244;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
textarea {
|
||||
@@ -48,12 +83,13 @@ fieldset {
|
||||
gap: 8px;
|
||||
margin: 0;
|
||||
padding: 10px;
|
||||
border: 1px solid #cbd5e1;
|
||||
border: 1px solid #585b70;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
legend {
|
||||
padding: 0 4px;
|
||||
color: #b4befe;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@@ -76,21 +112,24 @@ legend {
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
border: 1px solid #cba6f7;
|
||||
border-radius: 8px;
|
||||
padding: 10px 12px;
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
background: #cba6f7;
|
||||
color: #11111b;
|
||||
}
|
||||
|
||||
#submit-link {
|
||||
background: #2563eb;
|
||||
color: white;
|
||||
background: #cba6f7;
|
||||
color: #11111b;
|
||||
}
|
||||
|
||||
.secondary {
|
||||
background: #e2e8f0;
|
||||
color: #0f172a;
|
||||
background: #45475a;
|
||||
border-color: #585b70;
|
||||
color: #cdd6f4;
|
||||
}
|
||||
|
||||
.status {
|
||||
@@ -101,15 +140,33 @@ button {
|
||||
}
|
||||
|
||||
.status.success {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
background: #1e3a38;
|
||||
color: #94e2d5;
|
||||
}
|
||||
|
||||
.status.error {
|
||||
background: #fee2e2;
|
||||
color: #991b1b;
|
||||
background: #45243a;
|
||||
color: #f38ba8;
|
||||
}
|
||||
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.extension-footer {
|
||||
margin-top: 16px;
|
||||
color: #7f849c;
|
||||
font-size: 0.68rem;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.extension-footer a {
|
||||
color: inherit;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.extension-footer a:hover,
|
||||
.extension-footer a:focus-visible,
|
||||
.extension-footer a:active {
|
||||
color: #cba6f7;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Copyright © 2026 Olaf Kolkman -->
|
||||
<!-- SPDX-License-Identifier: GPL-3.0-or-later -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
@@ -8,7 +10,8 @@
|
||||
<body>
|
||||
<main class="popup-shell">
|
||||
<header>
|
||||
<h1>LinkLog</h1>
|
||||
<img class="extension-logo" src="logo.svg" alt="LinkLog" />
|
||||
<a id="feed-link" class="feed-link hidden" href="/" target="_blank" rel="noopener noreferrer">LinkLog</a>
|
||||
</header>
|
||||
|
||||
<div id="status" class="status hidden" aria-live="polite"></div>
|
||||
@@ -41,6 +44,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</main>
|
||||
<footer class="extension-footer">Copyright © 2026 Olaf Kolkman · <a href="https://git.kolkman.org/olaf/Link-Log">Repository</a></footer>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright © 2026 Olaf Kolkman
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
const statusEl = document.getElementById('status');
|
||||
const form = document.getElementById('link-form');
|
||||
const titleInput = document.getElementById('title');
|
||||
@@ -6,6 +9,7 @@ const commentInput = document.getElementById('comment');
|
||||
const openSettingsButton = document.getElementById('open-settings');
|
||||
const existingTags = document.getElementById('existing-tags');
|
||||
const newTagsInput = document.getElementById('new-tags');
|
||||
const feedLink = document.getElementById('feed-link');
|
||||
|
||||
function setStatus(message, isError = false) {
|
||||
statusEl.textContent = message;
|
||||
@@ -23,6 +27,18 @@ async function getSettings() {
|
||||
return result;
|
||||
}
|
||||
|
||||
async function updateFeedLink() {
|
||||
const settings = await browser.storage.local.get(['backendUrl', 'username', 'accessToken']);
|
||||
if (!settings.backendUrl || !settings.username || !settings.accessToken) return;
|
||||
try {
|
||||
const backend = new URL(settings.backendUrl);
|
||||
feedLink.href = `${backend.origin}/${encodeURIComponent(settings.username)}/`;
|
||||
feedLink.classList.remove('hidden');
|
||||
} catch (error) {
|
||||
feedLink.classList.add('hidden');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadExistingTags() {
|
||||
const settings = await getSettings();
|
||||
if (!settings.backendUrl || !settings.accessToken) {
|
||||
@@ -128,3 +144,4 @@ openSettingsButton.addEventListener('click', () => browser.runtime.openOptionsPa
|
||||
form.addEventListener('submit', handleSubmit);
|
||||
populateCurrentTab();
|
||||
loadExistingTags();
|
||||
updateFeedLink();
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"addons": {
|
||||
"linklog@kolkman.org": {
|
||||
"updates": [
|
||||
{
|
||||
"version": "0.1.0",
|
||||
"update_link": "https://git.kolkman.org/olaf/Link-Log/raw/branch/main/XPI/signed/LinkLog-0.1.0.xpi"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||