Compare commits
398 Commits
Author | SHA1 | Date | |
---|---|---|---|
94b7703ca9 | |||
a391dd1316 | |||
b6ba5211b7 | |||
8e8e130045 | |||
1f40bc1a0f | |||
5437212222 | |||
a8ab845cd2 | |||
8cee6dc98b | |||
70c2b73414 | |||
98013c4422 | |||
e9e22b762d | |||
620db19936 | |||
94a79dd62c | |||
b56c3efde0 | |||
066827f8f1 | |||
c3b65d9cd8 | |||
a15b916b06 | |||
31d0a5c233 | |||
140179e80a | |||
53cba2d7e4 | |||
e54312d3b8 | |||
cadc27b7b5 | |||
388b829ec1 | |||
67861f0f33 | |||
a1f1eb34d5 | |||
2a6789063e | |||
cbf1273a55 | |||
8143a23ced | |||
3c17810747 | |||
bea7a2e9ed | |||
2f0a2ac6b0 | |||
18908b6b56 | |||
b135a210cc | |||
3a2a829940 | |||
e56dd2dd2d | |||
3f41a48bc7 | |||
65ed53281a | |||
1121557a2e | |||
d4a7b86ee7 | |||
626c18b04e | |||
bfa97ed7c7 | |||
deae4d5367 | |||
899605c860 | |||
dc9a279991 | |||
2a53892581 | |||
6bef0eb764 | |||
462b40640c | |||
72e1b2025c | |||
fc7c4b1257 | |||
6c22c59056 | |||
94c2b1184f | |||
45231d703d | |||
7882fcbe8f | |||
3bbc8c4d35 | |||
8ae10dc80b | |||
9b11c2c629 | |||
e2a231fb4a | |||
8a9502d1f2 | |||
534438df63 | |||
45a4feec96 | |||
aa7a32395e | |||
ab9f57f044 | |||
4040d6aa08 | |||
1c96f5c35e | |||
4d3e42812d | |||
f7b3711d4f | |||
2408e076ff | |||
6f71ffb477 | |||
214433f36a | |||
309b22732e | |||
6fe7687b2a | |||
a8cbf757ff | |||
4a4bedfe2b | |||
051291f725 | |||
d2b338095f | |||
899827a8f2 | |||
5fcbe3d6a9 | |||
a0a40e6cb2 | |||
bb1190e3f8 | |||
0a3baed1da | |||
4931c489ed | |||
996f9abaa2 | |||
08c097e176 | |||
daa861a98b | |||
a25d08fd76 | |||
392d31cc53 | |||
92926fa8df | |||
61ae9ae465 | |||
89622697d5 | |||
17694f5646 | |||
5a1303149f | |||
8a0e190a86 | |||
0d7dfd8c9e | |||
f979ff7050 | |||
e3fcdea362 | |||
476fec2757 | |||
53c215399b | |||
2c330802da | |||
851d7046ea | |||
c0019d7246 | |||
7688e4d3a8 | |||
ef58749ce3 | |||
35941a7ddc | |||
1f2664e5a8 | |||
35656a5c34 | |||
799f22e989 | |||
e226a37251 | |||
8e3bc9d700 | |||
58c3e6c2ab | |||
0dc148bfea | |||
3eff1b08a9 | |||
02d789471f | |||
d367d47c4d | |||
c93b8fc045 | |||
eb9377e21d | |||
a1764eee42 | |||
86ef74e20d | |||
4de53b9926 | |||
99a195a3fd | |||
f1ced31f69 | |||
b3cedf2baa | |||
3bf19fabda | |||
cf81ebe8ad | |||
278b5566a1 | |||
e8c1390f09 | |||
3c04abda45 | |||
2597f99ccf | |||
9d3a07c1cf | |||
bdfd8925b5 | |||
1a4d1985f4 | |||
6273f3ea53 | |||
5bdc6fa471 | |||
3ba41291db | |||
0867811952 | |||
8d961cd805 | |||
97cea7b40b | |||
4106834db8 | |||
a4a8f7cab2 | |||
9e209ee800 | |||
ddfa84f040 | |||
6b3a6ec7c1 | |||
4d037c02bf | |||
deaeab10d8 | |||
2a5375b1e7 | |||
e7a03e3283 | |||
efb3a12dcc | |||
3830d695d7 | |||
f36620927b | |||
5423cbd628 | |||
abde709e54 | |||
27f2d319ab | |||
66234b14bc | |||
6a9167e565 | |||
3c60f8ca06 | |||
c26bf5c112 | |||
41cbde934a | |||
946941d95e | |||
50f0104239 | |||
40fa7edadf | |||
d6926569c6 | |||
a8bba324ca | |||
5bba5776b3 | |||
8104f6f228 | |||
3f4738e593 | |||
1516e17f5d | |||
676d2702b7 | |||
5d39548964 | |||
67d458bd38 | |||
d9684c7d62 | |||
5a818d2119 | |||
6f96d4ce65 | |||
f72395756a | |||
38d746b310 | |||
f7270987ea | |||
6f565c0f0a | |||
7f252e79b6 | |||
ba2bb17638 | |||
bc7c658293 | |||
4d84e69bb5 | |||
03fac74908 | |||
5252ff1ecf | |||
20100d3fd4 | |||
dd3b2656ad | |||
657f25e22b | |||
8be354fc49 | |||
e574758340 | |||
40cf519492 | |||
0e4fda54e9 | |||
868f91e1ef | |||
b9000c154f | |||
894c72a82f | |||
c128cfc25c | |||
36c88b463c | |||
8a66e74074 | |||
ea60b165da | |||
1011e0026b | |||
9462521287 | |||
576022c41a | |||
70c38b7ea8 | |||
36370f2dea | |||
db9bf7f7bd | |||
fa7aef0c37 | |||
b135ea17f6 | |||
4b1643bc47 | |||
240a8ce9c7 | |||
8928e8722b | |||
d692734e55 | |||
50197198b4 | |||
1ee1107c93 | |||
cf90533b6c | |||
f0211f621e | |||
d9693af89b | |||
13722232fb | |||
0bcb033349 | |||
e92c439724 | |||
7f34b585d3 | |||
d7e9fd918a | |||
9899c0c5e2 | |||
c50de0b0f0 | |||
bb7d2d7ae0 | |||
862d172ca8 | |||
3671051d0e | |||
223e20cbbc | |||
9af4312561 | |||
934e40240e | |||
edb1980387 | |||
bb7b04013f | |||
26a3007268 | |||
5de2b09596 | |||
3660577a23 | |||
98b4c7cf04 | |||
427a7b8d25 | |||
67b84830cd | |||
973cd53266 | |||
1afdbe6932 | |||
942f582329 | |||
951a80389a | |||
b7ecfc9925 | |||
59e389d793 | |||
2ec047cc00 | |||
ee33f54745 | |||
7a79534ca8 | |||
a74a9fc821 | |||
e2c388b9db | |||
0f573ce09e | |||
bc70e41b7c | |||
f500e14aa3 | |||
8b47938238 | |||
8912212d8e | |||
6a346bf940 | |||
b5bdae4611 | |||
6590da5793 | |||
eb2b426ec7 | |||
4864a0411f | |||
68590cae33 | |||
abf2bbaec2 | |||
0da7e2722f | |||
60d4b06057 | |||
4c3df34950 | |||
7737e60b52 | |||
71e816bc13 | |||
c74f90ef04 | |||
26cb7e5a17 | |||
2bad6672d8 | |||
71c4011526 | |||
5e81078f59 | |||
31b78e74df | |||
2ff689aab0 | |||
0a6f0ed3f7 | |||
bfec46673d | |||
3a16614c72 | |||
9c9efb845c | |||
6192f1b94d | |||
ce451b2449 | |||
8534e16469 | |||
f6cc6f2eae | |||
0904425221 | |||
a729886522 | |||
e5dfedc7d1 | |||
f02423d084 | |||
8f4b6e83eb | |||
3029919553 | |||
ac67db0591 | |||
1e08838f5b | |||
d814f7ee77
|
|||
7edfb9d386 | |||
6928d6caba | |||
1a626875cf | |||
6eb3b64334 | |||
09f3595e93 | |||
11e89622d4 | |||
0fa8acc264 | |||
afc7c64ed8 | |||
c794c1b885 | |||
6247529799 | |||
ad3eedc1fb | |||
1cfac3cae6 | |||
478bcd5d13 | |||
6932da69b3 | |||
857f47bf55 | |||
373d742751 | |||
575622c522 | |||
a3e86ccb1d | |||
e491798ff1 | |||
15df4ac236 | |||
017a74c4e4 | |||
6e5b1127f3 | |||
95f4f88949 | |||
92858882d7 | |||
4cba95ec8c | |||
bbb2f89d85 | |||
88213038b2 | |||
cc18b41e76 | |||
f16127a238 | |||
2a6ecfaede | |||
cf4a09bf03 | |||
20e60262fd | |||
9e3928c976 | |||
95d8768545 | |||
8679d09040 | |||
4cc5c6acb3 | |||
6f9b548b1a | |||
fbff3386a9 | |||
e5899fca58 | |||
dddec489b9 | |||
b4049eaeaa | |||
b3fd724b5a | |||
aca25be86a | |||
0f8cbdac57 | |||
630219d667 | |||
fef268e434 | |||
e18dcf3a48 | |||
e019320146 | |||
65b31e14f9 | |||
9cb5cbcde9 | |||
25914ff5a7 | |||
c4af799279 | |||
3f8f0e14f4 | |||
5414b30e7f | |||
7aee897c1b | |||
4060f9cc11 | |||
5b526cbf5b | |||
f5065ff42b | |||
86b5546f5f | |||
43ae2a293b | |||
b8ddbd4255 | |||
87cdba1db8 | |||
df83187e33 | |||
eccdbf29ab | |||
28d181f8bc | |||
d386daf2ff | |||
0c1e116c1e | |||
bb0ed67827 | |||
b111d06851 | |||
79388845ea | |||
99faef2e77 | |||
21fffbfe10 | |||
64fb9f0c2a | |||
a42e0bef2c | |||
45a09006e1 | |||
240484be4c | |||
22f4d115e3 | |||
32920e0e5d | |||
f03a5918d1 | |||
dd1870b52a | |||
f0c1a8f98f | |||
0c181d7e7c | |||
e3dc0e833a | |||
6de875edea | |||
986a55173f | |||
59c8cabf02 | |||
a33b9fab07 | |||
f8a725e1e7 | |||
b3ab3af01b | |||
79f9463e56 | |||
4257b2ed51 | |||
6488ab60ec | |||
18bd3dfcf9 | |||
ec114e160d | |||
de033af18f | |||
e971c6fcb7 | |||
d3c465391c | |||
f1fa19593d | |||
60b6f9c73e | |||
55b95ddecb | |||
0800a251b2 | |||
82cf7a80eb | |||
379f3d12eb | |||
e52972d4d4 | |||
1a0ca4dec2 | |||
0bac9d8d5a | |||
a9608363c5 | |||
f1a2c5ae8e | |||
192a81ede7 | |||
916aa5abbd | |||
d4a5cc6eee | |||
d19605cc8d | |||
8d529327a4 |
@ -1,4 +1,3 @@
|
||||
.svn
|
||||
db.sqlite
|
||||
out/**/*.o
|
||||
out/**/*.d
|
||||
.git
|
||||
db.sqlite*
|
||||
out/
|
||||
|
@ -6,15 +6,51 @@ jobs:
|
||||
Build-All:
|
||||
runs-on: ubuntu-latest
|
||||
container:
|
||||
valid_volumes: ['/opt/keys']
|
||||
image: node:23-bookworm-slim
|
||||
valid_volumes:
|
||||
- '/opt/keys'
|
||||
- '/opt/deps'
|
||||
volumes:
|
||||
- /opt/keys:/opt/keys
|
||||
- /opt/deps:/opt/deps
|
||||
steps:
|
||||
- name: check out code
|
||||
- name: Install build dependencies
|
||||
run: >
|
||||
apt update && apt install -y \
|
||||
build-essential \
|
||||
clang-19 \
|
||||
cmake \
|
||||
curl \
|
||||
docker.io \
|
||||
doxygen \
|
||||
file \
|
||||
gcc-aarch64-linux-gnu \
|
||||
git \
|
||||
graphviz \
|
||||
libgpgme11 \
|
||||
libssl-dev \
|
||||
mingw-w64 \
|
||||
rsync \
|
||||
unzip \
|
||||
zip \
|
||||
zlib1g-dev
|
||||
- name: Get code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: true
|
||||
- run: ln -s /opt/keys .keys
|
||||
- name: Setup environment
|
||||
run: |
|
||||
update-alternatives --install /usr/bin/clang clang /usr/bin/clang-19 100
|
||||
update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-19 100
|
||||
ln -s /opt/keys .keys
|
||||
ln -sf /opt/deps/ios_toolchain deps/
|
||||
ln -sf /opt/deps/macos_toolchain deps/
|
||||
- name: Build documentation
|
||||
run: |
|
||||
mkdir -p out/html/ ~/.ssh/
|
||||
make docs
|
||||
echo 'pildefriends ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKD3Kde5vDO0TrMBDK0IGGeNGe/XinWAZkSQ/rXxwUjt' >> ~/.ssh/known_hosts
|
||||
rsync -avP --delete -e "ssh -i /opt/keys/ssh.ed25519" out/html/ tfdocs@pildefriends:docs/html/
|
||||
- name: Setup JDK
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
@ -24,14 +60,12 @@ jobs:
|
||||
uses: android-actions/setup-android@v3
|
||||
with:
|
||||
packages: 'tools platform-tools build-tools;34.0.0 platforms;android-34 ndk;26.3.11579264'
|
||||
- run: sudo apt update && sudo apt install -y doxygen graphviz mingw-w64 libgpgme11 gcc-aarch64-linux-gnu
|
||||
- run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all docs
|
||||
- run: docker build .
|
||||
- uses: actions/upload-artifact@v3
|
||||
- name: Docker build
|
||||
run: DOCKER_BUILDKIT=1 docker build .
|
||||
- name: Build
|
||||
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist docs
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
path: |
|
||||
out/TildeFriends-release.fdroid.apk
|
||||
out/winrelease/tildefriends.standalone.exe
|
||||
out/tildefriends-x86_64.AppImage
|
||||
out/release/tildefriends.standalone
|
||||
out/armrelease/tildefriends.standalone
|
||||
name: dist
|
||||
path: dist/*
|
||||
|
4
.gitignore
vendored
@ -1,11 +1,13 @@
|
||||
build/
|
||||
*.core
|
||||
db.*
|
||||
deps/ios_toolchain/
|
||||
deps/ios_toolchain
|
||||
deps/macos_toolchain
|
||||
deps/openssl/
|
||||
dist/
|
||||
.flatpak-builder
|
||||
.keys
|
||||
**/.DS_Store
|
||||
logs/
|
||||
**/node_modules
|
||||
out
|
||||
|
6
.gitmodules
vendored
@ -26,6 +26,6 @@
|
||||
[submodule "deps/c-ares"]
|
||||
path = deps/c-ares
|
||||
url = https://github.com/c-ares/c-ares.git
|
||||
[submodule "docs"]
|
||||
path = docs
|
||||
url = https://dev.tildefriends.net/cory/tildefriends.wiki.git
|
||||
[submodule "deps/zsign"]
|
||||
path = deps/zsign
|
||||
url = https://github.com/zhlynn/zsign.git
|
||||
|
@ -1,19 +1,16 @@
|
||||
FROM bitnami/minideb:bullseye AS build
|
||||
FROM bitnami/minideb:bookworm AS build
|
||||
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
gcc \
|
||||
libc6-dev \
|
||||
libssl-dev \
|
||||
perl \
|
||||
make
|
||||
|
||||
COPY . /app
|
||||
RUN make -C /app -j $(nproc) release
|
||||
|
||||
FROM bitnami/minideb:bullseye
|
||||
RUN apt-get update && \
|
||||
apt-get install -y --no-install-recommends \
|
||||
libssl1.1
|
||||
FROM bitnami/minideb:bookworm
|
||||
|
||||
COPY --from=build /app/out/release/tildefriends /app/out/release/tildefriends
|
||||
COPY --from=build /app/apps /app/apps
|
||||
|
16
Doxyfile
@ -943,7 +943,9 @@ WARN_LOGFILE =
|
||||
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
|
||||
# Note: If this tag is empty the current directory is searched.
|
||||
|
||||
INPUT = src/
|
||||
INPUT = README.md \
|
||||
docs/ \
|
||||
src/
|
||||
|
||||
# This tag can be used to specify the character encoding of the source files
|
||||
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
|
||||
@ -1049,7 +1051,7 @@ EXAMPLE_RECURSIVE = NO
|
||||
# that contain images that are to be included in the documentation (see the
|
||||
# \image command).
|
||||
|
||||
IMAGE_PATH =
|
||||
IMAGE_PATH = docs/images/
|
||||
|
||||
# The INPUT_FILTER tag can be used to specify a program that doxygen should
|
||||
# invoke to filter for each input file. Doxygen will invoke the filter program
|
||||
@ -1110,7 +1112,7 @@ FILTER_SOURCE_PATTERNS =
|
||||
# (index.html). This can be useful if you have a project on for instance GitHub
|
||||
# and want to reuse the introduction page also for the doxygen output.
|
||||
|
||||
USE_MDFILE_AS_MAINPAGE =
|
||||
USE_MDFILE_AS_MAINPAGE = README.md
|
||||
|
||||
# The Fortran standard specifies that for fixed formatted Fortran code all
|
||||
# characters from position 72 are to be considered as comment. A common
|
||||
@ -1680,7 +1682,7 @@ DISABLE_INDEX = NO
|
||||
# The default value is: NO.
|
||||
# This tag requires that the tag GENERATE_HTML is set to YES.
|
||||
|
||||
GENERATE_TREEVIEW = NO
|
||||
GENERATE_TREEVIEW = YES
|
||||
|
||||
# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the
|
||||
# FULL_SIDEBAR option determines if the side bar is limited to only the treeview
|
||||
@ -2268,7 +2270,7 @@ GENERATE_AUTOGEN_DEF = NO
|
||||
# database with symbols found by doxygen stored in tables.
|
||||
# The default value is: NO.
|
||||
|
||||
GENERATE_SQLITE3 = NO
|
||||
#GENERATE_SQLITE3 = NO
|
||||
|
||||
# The SQLITE3_OUTPUT tag is used to specify where the Sqlite3 database will be
|
||||
# put. If a relative path is entered the value of OUTPUT_DIRECTORY will be put
|
||||
@ -2276,7 +2278,7 @@ GENERATE_SQLITE3 = NO
|
||||
# The default directory is: sqlite3.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_OUTPUT = sqlite3
|
||||
#SQLITE3_OUTPUT = sqlite3
|
||||
|
||||
# The SQLITE3_OVERWRITE_DB tag is set to YES, the existing doxygen_sqlite3.db
|
||||
# database file will be recreated with each doxygen run. If set to NO, doxygen
|
||||
@ -2284,7 +2286,7 @@ SQLITE3_OUTPUT = sqlite3
|
||||
# The default value is: YES.
|
||||
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
|
||||
|
||||
SQLITE3_RECREATE_DB = YES
|
||||
#SQLITE3_RECREATE_DB = YES
|
||||
|
||||
#---------------------------------------------------------------------------
|
||||
# Configuration options related to the Perl module output
|
||||
|
395
GNUmakefile
@ -16,11 +16,14 @@ MAKEFLAGS += --no-builtin-rules
|
||||
## LD := Linker.
|
||||
## ANDROID_SDK := Path to the Android SDK.
|
||||
|
||||
VERSION_CODE := 32
|
||||
VERSION_NUMBER := 0.0.27
|
||||
VERSION_CODE := 38
|
||||
VERSION_CODE_IOS := 14
|
||||
VERSION_NUMBER := 0.0.32
|
||||
VERSION_NAME := This program kills fascists.
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3480000.zip
|
||||
IPHONEOS_VERSION_MIN=14.0
|
||||
|
||||
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500100.zip
|
||||
BUNDLETOOL_URL := https://github.com/google/bundletool/releases/download/1.17.0/bundletool-all-1.17.0.jar
|
||||
APPIMAGETOOL_URL := https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage
|
||||
APPIMAGETOOL_MD5 := e989fadfc4d685fd3d6aeeb9b525d74d out/appimagetool
|
||||
@ -33,21 +36,27 @@ UNAME_M := $(shell uname -m)
|
||||
ANDROID_SDK ?= ~/Android/Sdk
|
||||
BUNDLETOOL = out/bundletool.jar
|
||||
|
||||
HAVE_WIN := 0
|
||||
HAVE_CROSS_AARCH64 := 0
|
||||
HAVE_WIN :=
|
||||
HAVE_CROSS_AARCH64 :=
|
||||
USE_SYSTEM_SSL :=
|
||||
|
||||
export SOURCE_DATE_EPOCH=1
|
||||
export TZ=UTC
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
BUILD_TYPES := macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease
|
||||
BUILD_TYPES := debug release iosdebug iosrelease iossimdebug iossimrelease
|
||||
HAVE_ANDROID = 0
|
||||
HAVE_LINUX_IOS = 0
|
||||
HAVE_LINUX_MACOS = 0
|
||||
HAVE_WIN = 0
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
BUILD_TYPES := debug release
|
||||
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1,0)
|
||||
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1,0)
|
||||
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1,0)
|
||||
HAVE_ANDROID = $(if $(shell which $(ANDROID_SDK)/platform-tools/adb),1)
|
||||
HAVE_LINUX_IOS = $(if $(shell which deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang),1)
|
||||
HAVE_LINUX_MACOS = $(if $(shell which deps/macos_toolchain/bin/oa64-clang),1)
|
||||
HAVE_WIN = $(if $(shell which x86_64-w64-mingw32-gcc-win32),1)
|
||||
ifneq ($(UNAME_M),aarch64)
|
||||
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1,0)
|
||||
HAVE_CROSS_AARCH64 = $(if $(shell which aarch64-linux-gnu-gcc),1)
|
||||
endif
|
||||
else ifeq ($(UNAME_S),Haiku)
|
||||
BUILD_TYPES := debug release
|
||||
@ -56,6 +65,11 @@ LDFLAGS += \
|
||||
-lbsd \
|
||||
-lnetwork \
|
||||
-Wno-stringop-overflow
|
||||
USE_SYSTEM_SSL := 1
|
||||
HAVE_ANDROID = 0
|
||||
HAVE_LINUX_IOS = 0
|
||||
HAVE_LINUX_MACOS = 0
|
||||
HAVE_WIN = 0
|
||||
else ifeq ($(UNAME_S),OpenBSD)
|
||||
BUILD_TYPES := debug release
|
||||
CFLAGS += \
|
||||
@ -63,18 +77,24 @@ CFLAGS += \
|
||||
LDFLAGS += \
|
||||
-lexecinfo \
|
||||
-lc++abi
|
||||
HAVE_ANDROID := 0
|
||||
HAVE_LINUX_IOS := 0
|
||||
HAVE_ANDROID :=
|
||||
HAVE_LINUX_IOS :=
|
||||
HAVE_LINUX_MACOS :=
|
||||
USE_SYSTEM_SSL := 1
|
||||
else
|
||||
$(error Unexpected host platform $(UNAME_S).)
|
||||
endif
|
||||
|
||||
# Everything is set above.
|
||||
$(info Building Tilde Friends $(VERSION_NUMBER) android=$(if $(HAVE_ANDROID),1,0) win=$(if $(HAVE_WIN),1,0) cross_aarch64=$(if $(HAVE_CROSS_AARCH64),1,0) cross_ios=$(if $(HAVE_LINUX_IOS),1,0) cross_macos=$(if $(HAVE_LINUX_MACOS),1,0) system_ssl=$(if $(USE_SYSTEM_SSL),1,0))
|
||||
|
||||
CFLAGS += \
|
||||
-std=gnu11 \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-Wno-unused-parameter \
|
||||
-Wno-cast-function-type-mismatch \
|
||||
-Wno-unknown-warning-option \
|
||||
-Wno-unused-parameter \
|
||||
-MMD \
|
||||
-MP \
|
||||
-ffunction-sections \
|
||||
@ -136,12 +156,28 @@ ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
BUILD_TYPES += armdebug armrelease
|
||||
endif
|
||||
|
||||
LINUX_TARGETS := \
|
||||
HOST_TARGETS := \
|
||||
out/debug/tildefriends \
|
||||
out/release/tildefriends
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
MACOS_TARGETS := \
|
||||
out/macosdebug/tildefriends \
|
||||
out/macosrelease/tildefriends
|
||||
out/debug/tildefriends \
|
||||
out/release/tildefriends
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
MACOS_TARGETS := \
|
||||
out/macosdebug-arm/tildefriends \
|
||||
out/macosrelease-arm/tildefriends \
|
||||
out/macosdebug-x86_64/tildefriends \
|
||||
out/macosrelease-x86_64/tildefriends
|
||||
all: out/macosdebug/tildefriends.standalone
|
||||
all: out/macosrelease/tildefriends.standalone
|
||||
else
|
||||
MACOS_TARGETS :=
|
||||
endif
|
||||
else
|
||||
MACOS_TARGETS :=
|
||||
endif
|
||||
IOS_TARGETS := \
|
||||
out/iosdebug/tildefriends \
|
||||
out/iosrelease/tildefriends
|
||||
@ -155,6 +191,14 @@ ifeq ($(HAVE_LINUX_IOS),1)
|
||||
BUILD_TYPES += iosdebug iosrelease
|
||||
all: $(IOS_APPS)
|
||||
endif
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
BUILD_TYPES += \
|
||||
macosdebug-arm \
|
||||
macosrelease-arm \
|
||||
macosdebug-x86_64 \
|
||||
macosrelease-x86_64
|
||||
all: $(IOS_APPS)
|
||||
endif
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
all: $(IOS_APPS) \
|
||||
out/tildefriends-iossimdebug.app/tildefriends \
|
||||
@ -169,35 +213,38 @@ DEBUG_TARGETS := \
|
||||
out/windebug/tildefriends.exe \
|
||||
out/iosdebug/tildefriends \
|
||||
out/iossimdebug/tildefriends \
|
||||
out/macosdebug/tildefriends \
|
||||
out/androiddebug/tildefriends \
|
||||
out/androiddebug-armv7a/tildefriends \
|
||||
out/androiddebug-x86_64/tildefriends \
|
||||
out/androiddebug-x86/tildefriends \
|
||||
out/armdebug/tildefriends
|
||||
out/armdebug/tildefriends \
|
||||
out/macosdebug-arm/tildefriends \
|
||||
out/macosdebug-x86_64/tildefriends
|
||||
RELEASE_TARGETS := \
|
||||
out/release/tildefriends \
|
||||
out/winrelease/tildefriends.exe \
|
||||
out/iosrelease/tildefriends \
|
||||
out/iossimrelease/tildefriends \
|
||||
out/macosrelease/tildefriends \
|
||||
out/androidrelease/tildefriends \
|
||||
out/androidrelease-armv7a/tildefriends \
|
||||
out/androidrelease-x86_64/tildefriends \
|
||||
out/androidrelease-x86/tildefriends \
|
||||
out/armrelease/tildefriends
|
||||
out/armrelease/tildefriends \
|
||||
out/macosrelease-arm/tildefriends \
|
||||
out/macosrelease-x86_64/tildefriends
|
||||
ALL_TARGETS = $(DEBUG_TARGETS) $(RELEASE_TARGETS)
|
||||
ANDROID_RELEASE_TARGETS := $(filter-out $(DEBUG_TARGETS),$(ANDROID_TARGETS))
|
||||
NONANDROID_RELEASE_TARGETS := $(filter-out $(ANDROID_ARM64_TARGETS),$(RELEASE_TARGETS))
|
||||
NONANDROID_TARGETS := $(filter-out $(ANDROID_TARGETS),$(ALL_TARGETS))
|
||||
NONMACOS_TARGETS := $(filter-out $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS),$(ALL_TARGETS))
|
||||
DEADSTRIP_TARGETS := $(filter-out $(ANDROID_TARGETS),$(NONMACOS_TARGETS))
|
||||
ifneq ($(UNAME_S),OpenBSD)
|
||||
$(NONMACOS_TARGETS): LDFLAGS += -static-libgcc
|
||||
endif
|
||||
|
||||
$(NONANDROID_TARGETS): CFLAGS += -fno-omit-frame-pointer
|
||||
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += -rdynamic
|
||||
$(filter-out $(WINDOWS_TARGETS),$(ALL_TARGETS)): LDFLAGS += \
|
||||
-rdynamic \
|
||||
-gz=zlib
|
||||
$(ANDROID_TARGETS): CFLAGS += \
|
||||
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
|
||||
-fPIC \
|
||||
@ -221,25 +268,29 @@ $(WINDOWS_TARGETS): CFLAGS += \
|
||||
-D_WIN32_WINNT=0x0A00 \
|
||||
-DWINVER=0x0A00 \
|
||||
-DNTDDI_VERSION=NTDDI_WIN10 \
|
||||
-Ideps/openssl/mingw64/usr/local/include
|
||||
-Iout/openssl/$(UNAME_S)/mingw64/usr/local/include
|
||||
$(WINDOWS_TARGETS): LDFLAGS += \
|
||||
-static \
|
||||
-lm \
|
||||
-Ldeps/openssl/mingw64/usr/local/lib
|
||||
-Lout/openssl/$(UNAME_S)/mingw64/usr/local/lib
|
||||
$(AARCH64_TARGETS): CC = aarch64-linux-gnu-gcc
|
||||
$(AARCH64_TARGETS): AS = $(CC)
|
||||
$(AARCH64_TARGETS): CFLAGS += -Ideps/openssl/Linux/aarch64/usr/local/include
|
||||
$(AARCH64_TARGETS): LDFLAGS += -Ldeps/openssl/Linux/aarch64/usr/local/lib
|
||||
$(AARCH64_TARGETS): CFLAGS += -Iout/openssl/Linux/aarch64/usr/local/include
|
||||
$(AARCH64_TARGETS): LDFLAGS += -Lout/openssl/Linux/aarch64/usr/local/lib
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
$(MACOS_TARGETS): CC = xcrun clang
|
||||
$(HOST_TARGETS): CC = xcrun clang
|
||||
$(IOS_TARGETS): IOS_SYSROOT := $(shell xcrun --sdk iphoneos --show-sdk-path)
|
||||
$(IOS_TARGETS): CC = xcrun --sdk iphoneos clang -isysroot $(IOS_SYSROOT) -arch arm64
|
||||
$(IOSSIM_TARGETS): IOSSIM_SYSROOT := $(shell xcrun --sdk iphonesimulator --show-sdk-path)
|
||||
$(IOSSIM_TARGETS): CC = xcrun --sdk iphonesimulator clang -isysroot $(IOSSIM_SYSROOT) -arch x86_64
|
||||
else ifeq ($(UNAME_S),Linux)
|
||||
$(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64
|
||||
$(IOS_TARGETS): CFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk -arch arm64 -DTARGET_OS_IPHONE=1
|
||||
$(IOS_TARGETS): LDFLAGS += -isysroot deps/ios_toolchain/target/SDKs/iPhoneOS18.2.sdk
|
||||
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
|
||||
$(IOS_TARGETS): CC = PATH=$$PATH:deps/ios_toolchain/target/bin LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/ios_toolchain/target/lib deps/ios_toolchain/target/bin/arm-apple-darwin11-clang
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/o64-clang
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CC = PATH=deps/macos_toolchain/bin:$$PATH LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:deps/macos_toolchain/lib deps/macos_toolchain/bin/oa64-clang
|
||||
endif
|
||||
$(ANDROID_X86_64_TARGETS): ANDROID_NDK_TARGET_TRIPLE := x86_64-linux-android
|
||||
$(ANDROID_X86_TARGETS): ANDROID_NDK_TARGET_TRIPLE := i686-linux-android
|
||||
@ -250,30 +301,39 @@ $(ANDROID_TARGETS): AS = $(CC)
|
||||
$(ANDROID_TARGETS): CFLAGS += \
|
||||
-target $(ANDROID_NDK_TARGET_TRIPLE)$(ANDROID_MIN_SDK_VERSION) \
|
||||
-Wno-unknown-warning-option
|
||||
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Ideps/openssl/android/armeabi-v7a/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Ldeps/openssl/android/armeabi-v7a/usr/local/lib
|
||||
$(ANDROID_ARM64_TARGETS): CFLAGS += -Ideps/openssl/android/arm64-v8a/usr/local/include
|
||||
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Ldeps/openssl/android/arm64-v8a/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Ideps/openssl/android/x86/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): CFLAGS += -Iout/openssl/android/armeabi-v7a/usr/local/include
|
||||
$(ANDROID_ARMV7A_TARGETS): LDFLAGS += -Lout/openssl/android/armeabi-v7a/usr/local/lib
|
||||
$(ANDROID_ARM64_TARGETS): CFLAGS += -Iout/openssl/android/arm64-v8a/usr/local/include
|
||||
$(ANDROID_ARM64_TARGETS): LDFLAGS += -Lout/openssl/android/arm64-v8a/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Iout/openssl/android/x86/usr/local/include
|
||||
$(ANDROID_X86_TARGETS): CFLAGS += -Wno-atomic-alignment
|
||||
$(ANDROID_X86_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86/usr/local/lib
|
||||
$(ANDROID_X86_64_TARGETS): CFLAGS += -Ideps/openssl/android/x86_64/usr/local/include
|
||||
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Ldeps/openssl/android/x86_64/usr/local/lib
|
||||
$(ANDROID_X86_TARGETS): LDFLAGS += -Lout/openssl/android/x86/usr/local/lib
|
||||
$(ANDROID_X86_64_TARGETS): CFLAGS += -Iout/openssl/android/x86_64/usr/local/include
|
||||
$(ANDROID_X86_64_TARGETS): LDFLAGS += -Lout/openssl/android/x86_64/usr/local/lib
|
||||
$(NONMACOS_TARGETS): CFLAGS += -Wno-cast-function-type
|
||||
$(DEADSTRIP_TARGETS): LDFLAGS += -Wl,--gc-sections
|
||||
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=9.0
|
||||
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=9.0
|
||||
$(MACOS_TARGETS): LDFLAGS += -Wl,-dead_strip
|
||||
$(NONMACOS_TARGETS): LDFLAGS += -Wl,--gc-sections -Wl,--as-needed
|
||||
$(IOS_TARGETS): CFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||
$(IOS_TARGETS): LDFLAGS += -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/ios/ios64-xcrun/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/ios/ios64-xcrun/usr/local/lib
|
||||
$(IOS_TARGETS): CFLAGS += -Iout/openssl/ios/ios64-xcrun/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/ios/ios64-xcrun/usr/local/lib
|
||||
else
|
||||
$(IOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/ios64-cross/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
|
||||
$(IOS_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/ios64-cross/usr/local/include
|
||||
$(IOS_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/ios64-cross/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosdebug-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosdebug-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-x86_64/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): CFLAGS += -Iout/openssl/$(UNAME_S)/macos-arm/usr/local/include
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/%,$(ALL_TARGETS)): LDFLAGS += -Lout/openssl/$(UNAME_S)/macos-arm/usr/local/lib
|
||||
endif
|
||||
$(IOSSIM_TARGETS): CFLAGS += -Ideps/openssl/ios/iossimulator-xcrun/usr/local/include
|
||||
$(IOSSIM_TARGETS): LDFLAGS += -Ldeps/openssl/ios/iossimulator-xcrun/usr/local/lib
|
||||
$(LINUX_TARGETS) $(MACOS_TARGETS): CFLAGS += -Ideps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
|
||||
$(LINUX_TARGETS) $(MACOS_TARGETS): LDFLAGS += -Ldeps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
|
||||
$(IOSSIM_TARGETS): CFLAGS += -Iout/openssl/ios/iossimulator-xcrun/usr/local/include
|
||||
$(IOSSIM_TARGETS): LDFLAGS += -Lout/openssl/ios/iossimulator-xcrun/usr/local/lib
|
||||
$(HOST_TARGETS): CFLAGS += -Iout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/include
|
||||
$(HOST_TARGETS): LDFLAGS += -Lout/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib
|
||||
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
@ -292,13 +352,14 @@ endif
|
||||
|
||||
get_objs = \
|
||||
$(foreach build_type,$(BUILD_TYPES),$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)))))) \
|
||||
$(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
|
||||
$(foreach build_type,windebug winrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_win))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androiddebug-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_android))))) \
|
||||
$(foreach build_type,androiddebug androidrelease androiddebug-x86 androidrelease-x86 androiddebug-x86_64 androidrelease-x86_64 androiddebug-armv7a androidrelease-armv7a,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))) \
|
||||
$(foreach build_type,macosdebug macosrelease iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
|
||||
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_ios))))) \
|
||||
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86)))))
|
||||
$(foreach build_type,iosdebug iosrelease iossimdebug iossimrelease macosdebug-arm macosrelease-arm macosdebug-x86_64 macosrelease-x86_64,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos))))) \
|
||||
$(foreach build_type,androiddebug-x86 androidrelease-x86,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_x86))))) \
|
||||
$(if $(findstring Darwin,$(UNAME_S)),$(foreach build_type,debug release,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_macos)))))) \
|
||||
$(if $(findstring Darwin,$(UNAME_S)),,$(foreach build_type,debug release armdebug armrelease,$(addprefix $(BUILD_DIR)/$(build_type)/,$(addsuffix .o,$(basename $(value $(1)_unix))))))
|
||||
|
||||
APP_SOURCES := $(wildcard src/*.c)
|
||||
APP_SOURCES_ios := $(wildcard src/*.m)
|
||||
@ -320,10 +381,12 @@ $(APP_OBJS): CFLAGS += \
|
||||
-Ideps/valgrind \
|
||||
-Wdouble-promotion \
|
||||
-Werror
|
||||
ifneq ($(UNAME_S),Darwin)
|
||||
ifeq ($(UNAME_M),x86_64)
|
||||
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/macos% $(BUILD_DIR)/ios%,$(APP_OBJS)): CFLAGS += \
|
||||
$(filter-out $(BUILD_DIR)/android% $(BUILD_DIR)/ios% $(BUILD_DIR)/macos%,$(APP_OBJS)): CFLAGS += \
|
||||
-fanalyzer
|
||||
endif
|
||||
endif
|
||||
|
||||
ARES_SOURCES := \
|
||||
deps/c-ares/src/lib/ares_addrinfo2hostent.c \
|
||||
@ -551,15 +614,16 @@ $(UV_OBJS): CFLAGS += \
|
||||
-Ideps/libuv/include \
|
||||
-Ideps/libuv/src \
|
||||
-Wno-dangling-pointer \
|
||||
-Wno-format-truncation \
|
||||
-Wno-incompatible-pointer-types \
|
||||
-Wno-maybe-uninitialized \
|
||||
-Wno-nonnull \
|
||||
-Wno-sign-compare \
|
||||
-Wno-unknown-attributes \
|
||||
-Wno-unused-but-set-parameter \
|
||||
-Wno-unused-but-set-variable \
|
||||
-Wno-unused-result \
|
||||
-Wno-unused-variable \
|
||||
-Wno-nonnull
|
||||
-Wno-unused-variable
|
||||
$(filter out/win%,$(UV_OBJS)): \
|
||||
CFLAGS += \
|
||||
-Wno-cast-function-type \
|
||||
@ -655,12 +719,12 @@ $(SQLITE_OBJS): CFLAGS += \
|
||||
-DSQLITE_MAX_COMPOUND_SELECT=300 \
|
||||
-DSQLITE_MAX_EXPR_DEPTH=40 \
|
||||
-DSQLITE_MAX_FUNCTION_ARG=8 \
|
||||
-DSQLITE_MAX_LENGTH=5242880 \
|
||||
-DSQLITE_MAX_LENGTH=10485760 \
|
||||
-DSQLITE_MAX_LIKE_PATTERN_LENGTH=50 \
|
||||
-DSQLITE_MAX_SQL_LENGTH=100000 \
|
||||
-DSQLITE_MAX_TRIGGER_DEPTH=10 \
|
||||
-DSQLITE_MAX_VARIABLE_NUMBER=100 \
|
||||
-DSQLITE_MAX_VDBE_OP=25000 \
|
||||
-DSQLITE_MAX_VDBE_OP=50000 \
|
||||
-DSQLITE_OMIT_DEPRECATED \
|
||||
-DSQLITE_OMIT_DESERIALIZE \
|
||||
-DSQLITE_OMIT_LOAD_EXTENSION \
|
||||
@ -679,7 +743,7 @@ $(SQLITE_OBJS): CFLAGS += \
|
||||
|
||||
QUICKJS_SOURCES := \
|
||||
deps/quickjs/cutils.c \
|
||||
deps/quickjs/libbf.c \
|
||||
deps/quickjs/dtoa.c \
|
||||
deps/quickjs/libregexp.c \
|
||||
deps/quickjs/libunicode.c \
|
||||
deps/quickjs/quickjs.c
|
||||
@ -756,12 +820,12 @@ $(MINIUNZIP_OBJS): CFLAGS += \
|
||||
LDFLAGS += \
|
||||
-pthread \
|
||||
-lm
|
||||
$(LINUX_TARGETS) $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS): LDFLAGS += \
|
||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS) $(AARCH64_TARGETS) $(filter-out $(HOST_TARGETS),$(MACOS_TARGETS)): LDFLAGS += \
|
||||
-lssl \
|
||||
-lcrypto
|
||||
ifneq ($(UNAME_S),Haiku)
|
||||
ifneq ($(UNAME_S),OpenBSD)
|
||||
debug release $(MACOS_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
$(HOST_TARGETS) $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
-ldl
|
||||
endif
|
||||
endif
|
||||
@ -794,27 +858,15 @@ $(IOS_TARGETS) $(IOSSIM_TARGETS): LDFLAGS += \
|
||||
##
|
||||
## Common targets:
|
||||
##
|
||||
debug: ## Build a debug executable for the current platform.
|
||||
release: ## Build a release executable for the current platform.
|
||||
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
|
||||
debug: ## Build a debug executable for the current host platform.
|
||||
release: ## Build a release executable for the current host platform.
|
||||
armdebug: ## Cross-compile aarch64 debug on Linux.
|
||||
armrelease: ## Cross-compile aarch64 release on Linux.
|
||||
all: $(BUILD_TYPES) ## Build all targets that appear possible to build on this machine.
|
||||
unix: debug release ## Build all UNIX targets.
|
||||
win: windebug winrelease ## Build all Windows targets.
|
||||
windebug: ## Cross-compile a debug win32 executable on Linux.
|
||||
winrelease: ## Cross-compile a release win32 executable on Linux.
|
||||
.PHONY: all win unix
|
||||
|
||||
##
|
||||
## Windows targets:
|
||||
##
|
||||
windebug: ## Build a debug win32 executable.
|
||||
winrelease: ## Build a release win32 executable.
|
||||
|
||||
##
|
||||
## MacOS targets:
|
||||
##
|
||||
macosdebug: ## Build a MacOS debug executable.
|
||||
macosrelease: ## Build a MacOS release executable.
|
||||
|
||||
ALL_APP_OBJS := \
|
||||
$(APP_OBJS) \
|
||||
$(ARES_OBJS) \
|
||||
@ -870,6 +922,17 @@ src/android/AndroidManifest.xml : $(firstword $(MAKEFILE_LIST))
|
||||
-e 's/android:targetSdkVersion="[[:digit:]]*"/android:targetSdkVersion="$(ANDROID_TARGET_SDK_VERSION)"/' \
|
||||
$@
|
||||
|
||||
src/ios/Info.plist : $(firstword $(MAKEFILE_LIST))
|
||||
@echo "[ios_version] $@"
|
||||
@cat $@ | \
|
||||
tr '\n' '^' | \
|
||||
sed -r \
|
||||
-e 's@(<key>CFBundleShortVersionString</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(VERSION_NUMBER:%-wip=%)\2@' \
|
||||
-e 's@(<key>CFBundleVersion</key>\^[[:space:]]*<string>)[[:digit:]]+(</string>)@\1$(VERSION_CODE_IOS)\2@' \
|
||||
-e 's@(<key>MinimumOSVersion</key>\^[[:space:]]*<string>)[0-9.]*(</string>)@\1$(IPHONEOS_VERSION_MIN)\2@' | \
|
||||
tr '^' '\n' > \
|
||||
$@.tmp && mv $@.tmp $@ || rm -f $@.tmp
|
||||
|
||||
##
|
||||
## Android targets:
|
||||
##
|
||||
@ -887,29 +950,30 @@ out/res/layout_activity_main.xml.flat: src/android/res/layout/activity_main.xml
|
||||
@echo "[aapt2] $@"
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/layout/activity_main.xml
|
||||
|
||||
out/res/drawable_icon.xml.flat: src/android/res/drawable/icon.xml
|
||||
out/res/drawable_%.xml.flat: src/android/res/drawable/%.xml
|
||||
@mkdir -p $(dir $@)
|
||||
@echo "[aapt2] $@"
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ src/android/res/drawable/icon.xml
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 compile -o out/res/ $(<)
|
||||
|
||||
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
|
||||
out/apk/res.apk out/gen/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
|
||||
@echo [aapt2 link] res.apk
|
||||
@mkdir -p out/apk/
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat out/res/drawable_logo.xml.flat \
|
||||
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
|
||||
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
-o out/apk/res.apk \
|
||||
--java out/gen/
|
||||
|
||||
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat src/android/AndroidManifest.xml
|
||||
out/apk/res.fdroid.apk out/gen_fdroid/com/unprompted/tildefriends/R.java: out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat src/android/AndroidManifest.xml
|
||||
@echo [aapt2 link] res.fdroid.apk
|
||||
@mkdir -p out/apk/
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon.xml.flat \
|
||||
@sed -e 's@drawable/icon@drawable/icon_fdroid@' src/android/AndroidManifest.xml > out/apk/AndroidManifest.fdroid.xml
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link -I $(ANDROID_PLATFORM)/android.jar out/res/layout_activity_main.xml.flat out/res/drawable_icon_fdroid.xml.flat out/res/drawable_logo.xml.flat \
|
||||
--min-sdk-version $(ANDROID_MIN_SDK_VERSION) \
|
||||
--target-sdk-version $(ANDROID_TARGET_SDK_VERSION) \
|
||||
--rename-manifest-package com.unprompted.tildefriends.fdroid \
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
--manifest out/apk/AndroidManifest.fdroid.xml \
|
||||
-o out/apk/res.fdroid.apk \
|
||||
--java out/gen_fdroid/
|
||||
|
||||
@ -926,11 +990,12 @@ out/apk/classes.dex: $(CLASS_FILES)
|
||||
@$(ANDROID_BUILD_TOOLS)/d8 --lib $(ANDROID_PLATFORM)/android.jar --output $(dir $@) out/classes/com/unprompted/tildefriends/*.class
|
||||
|
||||
PACKAGE_DIRS := \
|
||||
apps/ \
|
||||
core/ \
|
||||
deps/codemirror/ \
|
||||
deps/prettier/ \
|
||||
deps/lit/
|
||||
apps \
|
||||
core \
|
||||
deps/codemirror \
|
||||
deps/prettier \
|
||||
deps/lit \
|
||||
deps/speedscope
|
||||
|
||||
RAW_FILES := $(sort $(filter-out apps/blog% apps/issues% apps/welcome% apps/journal% %.map, $(shell find $(PACKAGE_DIRS) -type f -not -name '.*')))
|
||||
|
||||
@ -951,7 +1016,7 @@ $(BUNDLETOOL):
|
||||
@curl -q -L --create-dirs -o $@ $(BUNDLETOOL_URL)
|
||||
|
||||
out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGETS)) $(RAW_FILES) out/apk/res.apk src/android/AndroidManifest.xml $(BUNDLETOOL)
|
||||
@rm -rf out/aab/staging/
|
||||
@rm -rf out/aab/staging/ out/aab/base.zip
|
||||
@mkdir -p out/aab/staging
|
||||
@$(ANDROID_BUILD_TOOLS)/aapt2 link --proto-format -o out/aab/temporary.apk \
|
||||
-I $(ANDROID_PLATFORM)/android.jar \
|
||||
@ -960,6 +1025,7 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
--manifest src/android/AndroidManifest.xml \
|
||||
-R out/res/layout_activity_main.xml.flat \
|
||||
-R out/res/drawable_icon.xml.flat \
|
||||
-R out/res/drawable_logo.xml.flat \
|
||||
--auto-add-overlay
|
||||
@unzip out/aab/temporary.apk -d out/aab/staging/
|
||||
@mkdir -p out/aab/staging/root/deps
|
||||
@ -970,14 +1036,11 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
@cp out/apk/classes.dex out/aab/staging/dex/
|
||||
@rm -fv out/base.zip
|
||||
@mkdir -p out/aab/staging/lib/arm64-v8a out/aab/staging/lib/armeabi-v7a out/aab/staging/lib/x86_64 out/aab/staging/lib/x86
|
||||
@cp out/androidrelease/tildefriends out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@cp out/androidrelease-armv7a/tildefriends out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@cp out/androidrelease-x86_64/tildefriends out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@cp out/androidrelease-x86/tildefriends out/aab/staging/lib/x86/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/aab/staging/lib/x86/libtildefriends.so
|
||||
@mkdir -p out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64 out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease/tildefriends -o out/aab/staging/lib/arm64-v8a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-armv7a/tildefriends -o out/aab/staging/lib/armeabi-v7a/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-x86_64/tildefriends -o out/aab/staging/lib/x86_64/libtildefriends.so
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip out/androidrelease-x86/tildefriends -o out/aab/staging/lib/x86/libtildefriends.so
|
||||
@cp -r apps/ out/aab/staging/root/
|
||||
@rm -rf out/aab/staging/root/apps/welcome*
|
||||
@cp -r core/ out/aab/staging/root/
|
||||
@ -986,7 +1049,12 @@ out/TildeFriends.aab: out/apk/classes.dex $(filter-out %debug%, $(ANDROID_TARGET
|
||||
@cp -r deps/codemirror/ out/aab/staging/root/deps/
|
||||
@cd out/aab/staging/; zip -r ../base.zip *; cd ../../../
|
||||
@java -jar $(BUNDLETOOL) build-bundle --overwrite --config=src/android/BundleConfig.json --modules=out/aab/base.zip --output=$@
|
||||
@jarsigner -keystore .keys/android.jks $@ androidKey -storepass android
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-armv7a/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86_64/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym
|
||||
@$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip --only-keep-debug out/androidrelease-x86/tildefriends -o out/aab/staging/BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym
|
||||
@cd out/aab/staging; zip -u ../../../$@ BUNDLE-METADATA/com.android.tools.build.debugsymbols/arm64-v8a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/armeabi-v7a/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86_64/libtildefriends.so.sym BUNDLE-METADATA/com.android.tools.build.debugsymbols/x86/libtildefriends.so.sym; cd ../../../
|
||||
@$(ANDROID_BUILD_TOOLS)/apksigner sign -ks .keys/android.jks --ks-key-alias androidKey -ks-pass pass:android --min-sdk-version=$(ANDROID_MIN_SDK_VERSION) $@
|
||||
|
||||
aab: out/TildeFriends.aab ## Build an Android App Bundle.
|
||||
.PHONY: aab
|
||||
@ -1092,12 +1160,24 @@ out/data.zip: $(RAW_FILES)
|
||||
@echo [zip] $@
|
||||
@zip -u $@ -q -9 $(RAW_FILES)
|
||||
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip
|
||||
out/zsign_build/zsign: $(wildcard deps/zsign/*.cpp deps/zsign/*.h deps/zsign/*.txt deps/zsign/common/*)
|
||||
@+echo [cmake] $@
|
||||
@cmake -B out/zsign_build deps/zsign
|
||||
@cmake --build out/zsign_build -- COLOR=0 VERBOSE=0 MAKESILENT=-s
|
||||
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
ZSIGN_DEP = out/zsign_build/zsign
|
||||
else
|
||||
ZSIGN_DEP =
|
||||
endif
|
||||
|
||||
out/tildefriends-%.app/tildefriends: out/%/tildefriends out/tildefriends-%.app/Info.plist out/tildefriends-%.app/tildefriends.png out/data.zip $(ZSIGN_DEP)
|
||||
@mkdir -p $(dir $@)
|
||||
@cp -v $< $@
|
||||
@cp -v $(filter-out out/zsign%,$<) $@
|
||||
@cp -v out/data.zip $(@D)/
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
@zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
|
||||
@mkdir -p $(realpath $(dir $@))/_CodeSignature
|
||||
@out/zsign_build/zsign -q -k .keys/apple.p12 -f -m src/ios/embedded.mobileprovision $(realpath $(dir $@))
|
||||
endif
|
||||
.SECONDARY:
|
||||
out/tildefriends-%.ipa: out/tildefriends-ios%.app/tildefriends
|
||||
@ -1135,26 +1215,34 @@ iossimdebuggo: out/tildefriends-iossimdebug.app/tildefriends ## Build, install,
|
||||
xcrun simctl launch booted com.unprompted.tildefriends
|
||||
.PHONY: iossimdebuggo
|
||||
|
||||
ANDROID_DEPS := deps/openssl/android/arm64-v8a/usr/local/lib/libssl.a
|
||||
ANDROID_DEPS := out/openssl/android/arm64-v8a/usr/local/lib/libssl.a
|
||||
$(ANDROID_DEPS):
|
||||
+@ANDROID_NDK_ROOT=$(ANDROID_NDK) tools/ssl-android
|
||||
+@export ANDROID_NDK_ROOT=$(ANDROID_NDK)
|
||||
+@export BUILD_PLATFORM=android
|
||||
+@export TOOLCHAIN=$(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64
|
||||
+@PATH="$$TOOLCHAIN/x86_64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86_64 SSL_TARGET=android-x86_64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/i686-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=x86 SSL_TARGET=android-x86 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/arm-linux-androideabi/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=armeabi-v7a SSL_TARGET=android-arm OPTIONS="--target=armv7a-linux-androideabi -Wl,--fix-cortex-a8 -D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
+@PATH="$$TOOLCHAIN/aarch64-linux-android/bin:$$TOOLCHAIN/bin:$$PATH" BUILD_TARGET=arm64-v8a SSL_TARGET=android-arm64 OPTIONS="-D__ANDROID_API__=$(ANDROID_MIN_SDK_VERSION) -Wno-macro-redefined" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/android%,$(APP_OBJS)): | $(ANDROID_DEPS)
|
||||
|
||||
ifeq ($(UNAME_S),Linux)
|
||||
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
ifneq ($(USE_SYSTEM_SSL),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@/usr/bin/env bash tools/ssl-local
|
||||
+@tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
LOCAL_DEPS := deps/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/aarch64/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 tools/ssl-local
|
||||
+@OPTIONS="--cross-compile-prefix=aarch64-linux-gnu-" BUILD_TARGET=aarch64 SSL_TARGET=linux-aarch64 tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/armdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/armrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
LOCAL_DEPS := deps/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/ios64-cross/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=deps/ios_toolchain/target/bin:$$PATH \
|
||||
BUILD_TARGET=ios64-cross \
|
||||
@ -1163,33 +1251,63 @@ $(LOCAL_DEPS):
|
||||
CROSS_TOP=../../deps/ios_toolchain/target \
|
||||
CROSS_SDK=iPhoneOS18.2.sdk \
|
||||
CC=clang \
|
||||
OPTIONS=-miphoneos-version-min=9.0 \
|
||||
OPTIONS=-miphoneos-version-min=$(IPHONEOS_VERSION_MIN) \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-arm/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
|
||||
BUILD_TARGET=macos-arm \
|
||||
SSL_TARGET=darwin64-arm64 \
|
||||
CC=../../deps/macos_toolchain/bin/oa64-clang \
|
||||
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
|
||||
AR=../../deps/macos_toolchain/bin/arm64-apple-darwin24-ar \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/macosrelease-arm/% $(BUILD_DIR)/macosdebug-arm/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/macos-x86_64/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@PATH=../../deps/macos_toolchain/bin:$$PATH \
|
||||
BUILD_TARGET=macos-x86_64 \
|
||||
SSL_TARGET=darwin64-x86_64 \
|
||||
CC=../../deps/macos_toolchain/bin/o64-clang \
|
||||
RANLIB=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ranlib \
|
||||
AR=../../deps/macos_toolchain/bin/x86_64-apple-darwin24-ar \
|
||||
tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/macosrelease-x86_64/% $(BUILD_DIR)/macosdebug-x86_64/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
LOCAL_DEPS := deps/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
LOCAL_DEPS := out/openssl/$(UNAME_S)/$(UNAME_M)/usr/local/lib/libssl.a
|
||||
$(LOCAL_DEPS):
|
||||
+@tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/macosdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/macosrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
$(filter $(BUILD_DIR)/debug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/release/%,$(APP_OBJS)): | $(LOCAL_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_WIN),1)
|
||||
WINDOWS_DEPS := deps/openssl/mingw64/usr/local/lib/libssl.a
|
||||
WINDOWS_DEPS := out/openssl/$(UNAME_S)/mingw64/usr/local/lib/libssl.a
|
||||
$(WINDOWS_DEPS):
|
||||
+@tools/ssl-mingw64
|
||||
+@BUILD_TARGET=mingw64 SSL_TARGET=mingw64 OPTIONS="--cross-compile-prefix=x86_64-w64-mingw32-" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/win%,$(APP_OBJS)): | $(WINDOWS_DEPS)
|
||||
endif
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
IOS_DEPS := deps/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
|
||||
IOS_DEPS := out/openssl/ios/ios64-xcrun/usr/local/lib/libssl.a
|
||||
$(IOS_DEPS):
|
||||
+@tools/ssl-ios
|
||||
+@BUILD_PLATFORM=ios BUILD_TARGET=ios64-xcrun SSL_TARGET=ios64-xcrun OPTIONS="-fPIC -Wno-macro-redefined -miphoneos-version-min=$(IPHONEOS_VERSION_MIN)" tools/ssl-local
|
||||
+@BUILD_PLATFORM=ios BUILD_TARGET=iossimulator-xcrun SSL_TARGET=iossimulator-xcrun OPTIONS="-fPIC -Wno-macro-redefined" tools/ssl-local
|
||||
$(filter $(BUILD_DIR)/ios%,$(APP_OBJS)): | $(IOS_DEPS)
|
||||
endif
|
||||
|
||||
out/macos%/tildefriends: out/macos%-arm/tildefriends out/macos%-x86_64/tildefriends
|
||||
@echo [lipo] $@
|
||||
@mkdir -p $(@D)
|
||||
@deps/macos_toolchain/bin/lipo -create -output $@ $^
|
||||
|
||||
##
|
||||
## Linux package targets:
|
||||
##
|
||||
@ -1258,7 +1376,6 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
|
||||
--exclude=deps/libsodium/test \
|
||||
--exclude=deps/libuv/docs \
|
||||
--exclude=deps/libuv/test \
|
||||
--exclude=deps/openssl \
|
||||
--exclude=deps/speedscope/*.map \
|
||||
--exclude=deps/sqlite/shell.c \
|
||||
--exclude=deps/zlib/contrib/vstudio \
|
||||
@ -1269,7 +1386,20 @@ tarball: ## Build an all-inclusive source tarball (.tar.xz).
|
||||
.PHONY: tarball
|
||||
|
||||
dist: ## Build versions of all distributables for release.
|
||||
dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefriends.standalone.exe) out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone $(if $(HAVE_CROSS_AARCH64), out/armrelease/tildefriends.standalone)
|
||||
dist: release-apk aab out/TildeFriends-release.fdroid.apk appimage tarball out/release/tildefriends.standalone
|
||||
ifeq ($(HAVE_LINUX_IOS),1)
|
||||
dist: iosrelease-ipa
|
||||
endif
|
||||
ifeq ($(HAVE_LINUX_MACOS),1)
|
||||
dist: out/macosrelease/tildefriends.standalone
|
||||
endif
|
||||
ifeq ($(HAVE_WIN),1)
|
||||
dist: out/winrelease/tildefriends.standalone.exe
|
||||
endif
|
||||
ifeq ($(HAVE_CROSS_AARCH64),1)
|
||||
dist: out/armrelease/tildefriends.standalone
|
||||
endif
|
||||
dist:
|
||||
@mkdir -p dist/
|
||||
@echo "[cp] tildefriends-$(VERSION_NUMBER).tar.xz"
|
||||
@cp out/tildefriends-$(VERSION_NUMBER).tar.xz dist/tildefriends-$(VERSION_NUMBER).tar.xz
|
||||
@ -1277,8 +1407,10 @@ dist: release-apk iosrelease-ipa aab $(if $(HAVE_WIN), out/winrelease/tildefrien
|
||||
@cp out/TildeFriends-x86-release.zopfli.apk dist/TildeFriends-x86-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-arm-$(VERSION_NUMBER).apk"
|
||||
@cp out/TildeFriends-arm-release.zopfli.apk dist/TildeFriends-arm-$(VERSION_NUMBER).apk
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
@test $(HAVE_LINUX_IOS) && echo "[cp] TildeFriends-$(VERSION_NUMBER).ipa"
|
||||
@test $(HAVE_LINUX_IOS) && cp out/tildefriends-release.ipa dist/TildeFriends-$(VERSION_NUMBER).ipa
|
||||
@test $(HAVE_LINUX_MACOS) && echo "[cp] tildefriends-macos-$(VERSION_NUMBER)"
|
||||
@test $(HAVE_LINUX_MACOS) && cp out/macosrelease/tildefriends.standalone dist/tildefriends-macos-$(VERSION_NUMBER)
|
||||
@test $(HAVE_WIN) && echo "[cp] tildefriends-$(VERSION_NUMBER).exe"
|
||||
@test $(HAVE_WIN) && cp out/winrelease/tildefriends.standalone.exe dist/tildefriends-$(VERSION_NUMBER).exe
|
||||
@echo "[cp] TildeFriends-$(VERSION_NUMBER).aab"
|
||||
@ -1300,6 +1432,17 @@ dist-test: dist ## Exercise some built distributable files, making sure they wor
|
||||
@rm -rf tildefriends-$(VERSION_NUMBER)
|
||||
.PHONY: dist-test
|
||||
|
||||
dist-ios: iosrelease-app
|
||||
rm -rfv out/Payload out/tildefriends.ipa
|
||||
mkdir -p out/Payload/tildefriends.app
|
||||
cp -avR out/tildefriends-iosrelease.app/* out/Payload/tildefriends.app/
|
||||
cp src/ios/tildefriends.png out/Payload/tildefriends.app/
|
||||
xcrun -sdk iphoneos actool --compile out/Payload/tildefriends.app/ --platform iphoneos --minimum-deployment-target $(IPHONEOS_VERSION_MIN) --app-icon AppIcon src/ios/icons/Assets.xcassets src/ios/icons/*.png --output-partial-info-plist out/actool.plist
|
||||
cp src/ios/distribution.mobileprovision out/Payload/tildefriends.app/embedded.mobileprovision
|
||||
xcrun -sdk iphoneos codesign -f -s 'Apple Distribution' --entitlements src/ios/Entitlements.plist --generate-entitlement-der out/Payload/tildefriends.app
|
||||
cd out; zip -r tildefriends.ipa Payload; cd ..
|
||||
xcrun -sdk iphoneos altool --upload-app -f out/tildefriends.ipa -t ios -u $$(cat .keys/altool-user) -p $$(cat .keys/altool-password)
|
||||
|
||||
##
|
||||
## Targets for tidying up:
|
||||
##
|
||||
@ -1340,6 +1483,18 @@ help: ## Display this help message.
|
||||
.PHONY: help
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
docs: debug
|
||||
docs: ## Build HTML docs.
|
||||
@echo '# CLI Usage\n' > docs/usage.md
|
||||
@echo "## tildefriends -h" >> docs/usage.md
|
||||
@echo '\n```' >> docs/usage.md
|
||||
@out/debug/tildefriends -h >> docs/usage.md
|
||||
@echo '```' >> docs/usage.md
|
||||
@for command in $$(out/debug/tildefriends -h | grep -Po '[A-Za-z_]*(?= - )'); do
|
||||
@ echo "\n## tildefriends $$command -h" >> docs/usage.md
|
||||
@ echo '\n```' >> docs/usage.md
|
||||
@ out/debug/tildefriends $$command -h >> docs/usage.md
|
||||
@ echo '```' >> docs/usage.md
|
||||
@done
|
||||
@doxygen
|
||||
.PHONY: docs
|
||||
|
27
README.md
@ -1,18 +1,16 @@
|
||||
# Tilde Friends
|
||||
|
||||
Tilde Friends is a tool for making and sharing.
|
||||
Tilde Friends participates in the Secure Scuttlebutt decentralized social
|
||||
network while also functioning as a platform for making, sharing, and running
|
||||
web applications.
|
||||
|
||||
A public instance lives at https://www.tildefriends.net/.
|
||||
|
||||
It is both a peer-to-peer social network client, participating in Secure
|
||||
Scuttlebutt, as well as a platform for writing and running web applications.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Make it easy and fun to run all sorts of web applications.
|
||||
2. Provide security that is easy to understand and protects your data.
|
||||
3. Make creating and sharing web applications accessible to anyone with a
|
||||
browser.
|
||||
1. Be the fanciest, best-maintained Secure Scuttlebutt client in town.
|
||||
1. Make it easy to make, share, and run all sorts of applications while
|
||||
respecting the privacy and safety of your data.
|
||||
|
||||
## Getting the Source
|
||||
|
||||
@ -40,8 +38,7 @@ dependencies in the right places.
|
||||
|
||||
### Requirements
|
||||
|
||||
On Linux only, system OpenSSL libraries (`libssl-dev`, in debian-speak) are
|
||||
assumed to be available.
|
||||
System OpenSSL libraries are assumed to be available on Haiku and OpenBSD.
|
||||
|
||||
On MacOS, Xcode's command-line tools are expected to be available.
|
||||
|
||||
@ -58,18 +55,16 @@ standard.
|
||||
## Running
|
||||
|
||||
By default, running the built `out/debug/tildefriends` executable will start a
|
||||
web server at <http://localhost:12345/>. It expects to be run with the
|
||||
repository root as the current working directory. `tildefriends -h` lists
|
||||
further options.
|
||||
web server at <http://localhost:12345/>. `tildefriends -h` lists further
|
||||
options.
|
||||
|
||||
The first user to create an account and log in will be granted administrative
|
||||
privileges. Further administration can be done at
|
||||
privileges. Further administration can be done in the `admin` app at
|
||||
<http://localhost:12345/~core/admin/>.
|
||||
|
||||
## Documentation
|
||||
|
||||
Docs are a work in progress:
|
||||
<https://dev.tildefriends.net/cory/tildefriends/wiki>.
|
||||
Docs live here: <https://docs.tildefriends.net/>.
|
||||
|
||||
## License
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🎛",
|
||||
"previous": "&R49FywYF8CXPhoSEydLbSCgvCddeyTiBwGuDU/gqY+M=.sha256"
|
||||
"previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256"
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ ${description.value}</textarea
|
||||
</button>
|
||||
</li>
|
||||
`;
|
||||
} else {
|
||||
} else if (description.type != 'hidden') {
|
||||
return html`
|
||||
<li class="w3-row">
|
||||
<label class="w3-quarter" for=${'gs_' + key} style="font-weight: bold">${title_case(key)}</label>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
42
apps/blog/lit-all.min.js
vendored
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
5
apps/intro.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💡",
|
||||
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
|
||||
}
|
16
apps/intro/app.js
Normal file
@ -0,0 +1,16 @@
|
||||
import * as tfrpc from '/tfrpc.js';
|
||||
|
||||
async function main() {
|
||||
await app.setDocument(utf8Decode(getFile('index.html')));
|
||||
}
|
||||
|
||||
tfrpc.register(async function complete() {
|
||||
if (
|
||||
core.user?.credentials?.permissions?.administration &&
|
||||
(await core.globalSettingsGet('index')) == '/~core/intro/'
|
||||
) {
|
||||
return await core.globalSettingsSet('index', '/~core/ssb/');
|
||||
}
|
||||
});
|
||||
|
||||
main();
|
286
apps/intro/index.html
Normal file
@ -0,0 +1,286 @@
|
||||
<!doctype html>
|
||||
<html style="height: 100%; margin: 0; padding: 0; box-sizing: border-box">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<link rel="stylesheet" type="text/css" href="w3.css" />
|
||||
<style>
|
||||
.slide {
|
||||
display: none;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
.dot {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
cursor: pointer;
|
||||
}
|
||||
.w3-left,
|
||||
.w3-right {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body
|
||||
style="
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
"
|
||||
class="w3-flex w3-dark-gray w3-center"
|
||||
>
|
||||
<div
|
||||
style="
|
||||
flex: 1 1 auto;
|
||||
overflow: auto;
|
||||
contain: content;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
"
|
||||
>
|
||||
<div class="slide">
|
||||
<div
|
||||
class="w3-content w3-xlarge w3-card-4 w3-blue w3-panel w3-padding-32 w3-round-xlarge"
|
||||
style="margin: 32px"
|
||||
>
|
||||
<div>
|
||||
<div>Welcome to</div>
|
||||
<div>~😎 Tilde Friends.</div>
|
||||
</div>
|
||||
<footer>
|
||||
<button class="w3-button w3-yellow proceed">Next</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-card-4 w3-gray" style="width: 90%">
|
||||
<header class="w3-container w3-blue w3-xlarge">
|
||||
<h1>This brief tutorial will introduce:</h1>
|
||||
</header>
|
||||
<ul class="w3-large w3-left-align">
|
||||
<li><b>Secure Scuttlebutt</b>, a decentralized social network.</li>
|
||||
<li>
|
||||
<b>Tilde Friends</b>, the application platform that you are using
|
||||
right now.
|
||||
</li>
|
||||
<li>
|
||||
<b>How to get started</b> if you want to get gossiping right away.
|
||||
</li>
|
||||
</ul>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow proceed">Onward</button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue">
|
||||
<h1>💻Secure Scuttlebutt in a Nutshell🦀</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>
|
||||
Secure Scuttlebutt is a social network whose technical operation
|
||||
attempts to mirror human social interaction.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
You can create your own account and post to your own feed on
|
||||
your own device. This is all <b>local</b> with no external
|
||||
communication. This puts you fully in control of your own words
|
||||
and actions.
|
||||
</li>
|
||||
<li>
|
||||
Before you can interact with others, you need to
|
||||
<b>connect over the network</b>, either directly to your friends
|
||||
(i.e., peer-to-peer between your phones on coffee shop Wi-Fi) or
|
||||
to 🚪<i>rooms</i> and 🍻<i>pubs</i> (hint: search the web for
|
||||
<i>#ssbroom</i>).
|
||||
</li>
|
||||
<li>
|
||||
Who you choose to <b>follow</b> determines what you see, with
|
||||
most people choosing to see messages from friends and friends of
|
||||
those friends. If you encounter content you'd rather not see,
|
||||
<b>block</b> the offending account to improve the experience for
|
||||
you and your followers.
|
||||
</li>
|
||||
<li>
|
||||
Your feed is an <b>immutable</b> log of your activity. Post with
|
||||
care, because like your words in real life, posts can't be taken
|
||||
back.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<a
|
||||
class="w3-button w3-light-gray"
|
||||
href="https://scuttlebutt.nz/"
|
||||
target="_blank"
|
||||
>See scuttlebutt.nz</a
|
||||
>
|
||||
<button class="w3-button w3-yellow proceed">Got It</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue w3-center">
|
||||
<h1>~😎 Let's Talk Tilde Friends ~😎</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>
|
||||
Tilde Friends is an application platform that is an application of
|
||||
its own.
|
||||
</p>
|
||||
<ul>
|
||||
<li>
|
||||
This intro is a Tilde Friends app. You can click <b>edit</b> at
|
||||
the top to look under the hood and make changes.
|
||||
</li>
|
||||
<li>
|
||||
It is already possible to make and share new applications using
|
||||
only Tilde Friends and Secure Scuttlebutt without having to set
|
||||
up development environments, configure web servers, register
|
||||
domain names, or pay for hosting services.
|
||||
</li>
|
||||
<li>
|
||||
But it's also set up so that you can't just break an app that
|
||||
everybody is using or do malicious things with personal content.
|
||||
There are <b>protections</b> in place like an operating system.
|
||||
The intent is also for it to be <b>safe</b> to run strange apps
|
||||
without worrying about adverse effects.
|
||||
</li>
|
||||
<li>
|
||||
But this is all a big 🚧work in progress🚧 and
|
||||
<b>experiment</b>. Let's see where it takes us.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow proceed">Okay</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
<div class="slide w3-gray" style="width: 90%">
|
||||
<div class="w3-card-4 w3-xlarge">
|
||||
<header class="w3-container w3-blue w3-center">
|
||||
<h1>🦀Let's Get this Tilde Friends Party Started🎉</h1>
|
||||
</header>
|
||||
<div class="w3-container w3-large w3-left-align">
|
||||
<p>The button below will take you to the Secure Scuttlebutt app.</p>
|
||||
<ul>
|
||||
<li>
|
||||
Remember:
|
||||
<ol>
|
||||
<li>You are in charge. This is all on your device.</li>
|
||||
<li>
|
||||
Make network connections to exchange messages with others.
|
||||
</li>
|
||||
<li>
|
||||
Follow more accounts to see more content, and block those
|
||||
posting content you'd rather not see.
|
||||
</li>
|
||||
<li>
|
||||
Be respectful, and consider the consequences of what you
|
||||
post.
|
||||
</li>
|
||||
<li>
|
||||
This is all under active development. Exercise patience, and
|
||||
report issues encountered.
|
||||
</li>
|
||||
</ol>
|
||||
</li>
|
||||
<li>
|
||||
To see this tutorial again later, select <b>apps</b> ->
|
||||
<b>Core Apps</b> -> <b>intro</b>.
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<footer class="w3-center w3-xlarge w3-padding">
|
||||
<button class="w3-button w3-yellow" id="complete">Let's Go!</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="w3-text-white w3-xlarge w3-center w3-flex"
|
||||
style="
|
||||
width: 100%;
|
||||
flex: 0 1;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
"
|
||||
>
|
||||
<div class="w3-jumbo" id="left" style="flex: 1 0; cursor: pointer">
|
||||
❮
|
||||
</div>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<span class="w3-badge dot w3-border w3-hover-yellow"></span>
|
||||
<div class="w3-jumbo" style="flex: 1 0; cursor: pointer" id="right">
|
||||
❯
|
||||
</div>
|
||||
</div>
|
||||
<script type="module">
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
|
||||
let index = 0;
|
||||
function set(i) {
|
||||
show(i - index);
|
||||
}
|
||||
function show(delta) {
|
||||
let slides = [...document.getElementsByClassName('slide')];
|
||||
let dots = [...document.getElementsByClassName('dot')];
|
||||
index = (index + delta + slides.length) % slides.length;
|
||||
for (let slide of slides) {
|
||||
slide.style.display =
|
||||
slides.indexOf(slide) == index ? 'block' : 'none';
|
||||
}
|
||||
for (let dot of dots) {
|
||||
if (dots.indexOf(dot) == index) {
|
||||
dot.classList.add('w3-white');
|
||||
} else {
|
||||
dot.classList.remove('w3-white');
|
||||
}
|
||||
}
|
||||
document.getElementById('left').style.visibility =
|
||||
index == 0 ? 'hidden' : 'visible';
|
||||
document.getElementById('right').style.visibility =
|
||||
index == slides.length - 1 ? 'hidden' : 'visible';
|
||||
}
|
||||
|
||||
let dots = [...document.getElementsByClassName('dot')];
|
||||
for (let dot of dots) {
|
||||
dot.onclick = () => set(dots.indexOf(dot));
|
||||
}
|
||||
for (let button of document.getElementsByClassName('proceed')) {
|
||||
button.onclick = () => show(1);
|
||||
}
|
||||
document.getElementById('left').onclick = () => show(-1);
|
||||
document.getElementById('right').onclick = () => show(1);
|
||||
document.getElementById('complete').onclick = function () {
|
||||
console.log('completing');
|
||||
tfrpc.rpc.complete().finally(function () {
|
||||
console.log('completed');
|
||||
let a = document.createElement('a');
|
||||
a.href = '/~core/ssb/';
|
||||
a.target = '_top';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
});
|
||||
};
|
||||
window.addEventListener('keyup', function (event) {
|
||||
if (event.key == 'ArrowLeft') {
|
||||
show(-1);
|
||||
} else if (event.key == 'ArrowRight') {
|
||||
show(1);
|
||||
}
|
||||
});
|
||||
show(0);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
251
apps/intro/w3.css
Normal file
@ -0,0 +1,251 @@
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}summary{display:list-item}
|
||||
audio,canvas,progress,video{display:inline-block}progress{vertical-align:baseline}
|
||||
audio:not([controls]){display:none;height:0}[hidden],template{display:none}
|
||||
a{background-color:transparent}a:active,a:hover{outline-width:0}
|
||||
abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}
|
||||
b,strong{font-weight:bolder}dfn{font-style:italic}mark{background:#ff0;color:#000}
|
||||
small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
|
||||
sub{bottom:-0.25em}sup{top:-0.5em}figure{margin:1em 40px}img{border-style:none}
|
||||
code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}hr{box-sizing:content-box;height:0;overflow:visible}
|
||||
button,input,select,textarea,optgroup{font:inherit;margin:0}optgroup{font-weight:bold}
|
||||
button,input{overflow:visible}button,select{text-transform:none}
|
||||
button,[type=button],[type=reset],[type=submit]{-webkit-appearance:button}
|
||||
button::-moz-focus-inner,[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner{border-style:none;padding:0}
|
||||
button:-moz-focusring,[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring{outline:1px dotted ButtonText}
|
||||
fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em}
|
||||
legend{color:inherit;display:table;max-width:100%;padding:0;white-space:normal}textarea{overflow:auto}
|
||||
[type=checkbox],[type=radio]{padding:0}
|
||||
[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}
|
||||
[type=search]{-webkit-appearance:textfield;outline-offset:-2px}
|
||||
[type=search]::-webkit-search-decoration{-webkit-appearance:none}
|
||||
::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}
|
||||
/* End extract */
|
||||
html,body{font-family:Verdana,sans-serif;font-size:15px;line-height:1.5}html{overflow-x:hidden}
|
||||
h1{font-size:36px}h2{font-size:30px}h3{font-size:24px}h4{font-size:20px}h5{font-size:18px}h6{font-size:16px}
|
||||
.w3-serif{font-family:serif}.w3-sans-serif{font-family:sans-serif}.w3-cursive{font-family:cursive}.w3-monospace{font-family:monospace}
|
||||
h1,h2,h3,h4,h5,h6{font-family:"Segoe UI",Arial,sans-serif;font-weight:400;margin:10px 0}.w3-wide{letter-spacing:4px}
|
||||
hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-image{max-width:100%;height:auto}img{vertical-align:middle}a{color:inherit}
|
||||
.w3-table,.w3-table-all{border-collapse:collapse;border-spacing:0;width:100%;display:table}.w3-table-all{border:1px solid #ccc}
|
||||
.w3-bordered tr,.w3-table-all tr{border-bottom:1px solid #ddd}.w3-striped tbody tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-table-all tr:nth-child(odd){background-color:#fff}.w3-table-all tr:nth-child(even){background-color:#f1f1f1}
|
||||
.w3-hoverable tbody tr:hover,.w3-ul.w3-hoverable li:hover{background-color:#ccc}.w3-centered tr th,.w3-centered tr td{text-align:center}
|
||||
.w3-table td,.w3-table th,.w3-table-all td,.w3-table-all th{padding:8px 8px;display:table-cell;text-align:left;vertical-align:top}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
.w3-ul{list-style-type:none;padding:0;margin:0}.w3-ul li{padding:8px 16px;border-bottom:1px solid #ddd}.w3-ul li:last-child{border-bottom:none}
|
||||
.w3-tooltip,.w3-display-container{position:relative}.w3-tooltip .w3-text{display:none}.w3-tooltip:hover .w3-text{display:inline-block}
|
||||
.w3-ripple:active{opacity:0.5}.w3-ripple{transition:opacity 0s}
|
||||
.w3-input{padding:8px;display:block;border:none;border-bottom:1px solid #ccc;width:100%}
|
||||
.w3-select{padding:9px 0;width:100%;border:none;border-bottom:1px solid #ccc}
|
||||
.w3-dropdown-click,.w3-dropdown-hover{position:relative;display:inline-block;cursor:pointer}
|
||||
.w3-dropdown-hover:hover .w3-dropdown-content{display:block}
|
||||
.w3-dropdown-hover:first-child,.w3-dropdown-click:hover{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-hover:hover > .w3-button:first-child,.w3-dropdown-click:hover > .w3-button:first-child{background-color:#ccc;color:#000}
|
||||
.w3-dropdown-content{cursor:auto;color:#000;background-color:#fff;display:none;position:absolute;min-width:160px;margin:0;padding:0;z-index:1}
|
||||
.w3-check,.w3-radio{width:24px;height:24px;position:relative;top:6px}
|
||||
.w3-sidebar{height:100%;width:200px;background-color:#fff;position:fixed!important;z-index:1;overflow:auto}
|
||||
.w3-bar-block .w3-dropdown-hover,.w3-bar-block .w3-dropdown-click{width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-dropdown-content,.w3-bar-block .w3-dropdown-click .w3-dropdown-content{min-width:100%}
|
||||
.w3-bar-block .w3-dropdown-hover .w3-button,.w3-bar-block .w3-dropdown-click .w3-button{width:100%;text-align:left;padding:8px 16px}
|
||||
.w3-main,#main{transition:margin-left .4s}
|
||||
.w3-modal{z-index:3;display:none;padding-top:100px;position:fixed;left:0;top:0;width:100%;height:100%;overflow:auto;background-color:rgb(0,0,0);background-color:rgba(0,0,0,0.4)}
|
||||
.w3-modal-content{margin:auto;background-color:#fff;position:relative;padding:0;outline:0;width:600px}
|
||||
.w3-bar{width:100%;overflow:hidden}.w3-center .w3-bar{display:inline-block;width:auto}
|
||||
.w3-bar .w3-bar-item{padding:8px 16px;float:left;width:auto;border:none;display:block;outline:0}
|
||||
.w3-bar .w3-dropdown-hover,.w3-bar .w3-dropdown-click{position:static;float:left}
|
||||
.w3-bar .w3-button{white-space:normal}
|
||||
.w3-bar-block .w3-bar-item{width:100%;display:block;padding:8px 16px;text-align:left;border:none;white-space:normal;float:none;outline:0}
|
||||
.w3-bar-block.w3-center .w3-bar-item{text-align:center}.w3-block{display:block;width:100%}
|
||||
.w3-responsive{display:block;overflow-x:auto}
|
||||
.w3-container:after,.w3-container:before,.w3-panel:after,.w3-panel:before,.w3-row:after,.w3-row:before,.w3-row-padding:after,.w3-row-padding:before,
|
||||
.w3-cell-row:before,.w3-cell-row:after,.w3-clear:after,.w3-clear:before,.w3-bar:before,.w3-bar:after{content:"";display:table;clear:both}
|
||||
.w3-col,.w3-half,.w3-third,.w3-twothird,.w3-threequarter,.w3-quarter{float:left;width:100%}
|
||||
.w3-col.s1{width:8.33333%}.w3-col.s2{width:16.66666%}.w3-col.s3{width:24.99999%}.w3-col.s4{width:33.33333%}
|
||||
.w3-col.s5{width:41.66666%}.w3-col.s6{width:49.99999%}.w3-col.s7{width:58.33333%}.w3-col.s8{width:66.66666%}
|
||||
.w3-col.s9{width:74.99999%}.w3-col.s10{width:83.33333%}.w3-col.s11{width:91.66666%}.w3-col.s12{width:99.99999%}
|
||||
@media (min-width:601px){.w3-col.m1{width:8.33333%}.w3-col.m2{width:16.66666%}.w3-col.m3,.w3-quarter{width:24.99999%}.w3-col.m4,.w3-third{width:33.33333%}
|
||||
.w3-col.m5{width:41.66666%}.w3-col.m6,.w3-half{width:49.99999%}.w3-col.m7{width:58.33333%}.w3-col.m8,.w3-twothird{width:66.66666%}
|
||||
.w3-col.m9,.w3-threequarter{width:74.99999%}.w3-col.m10{width:83.33333%}.w3-col.m11{width:91.66666%}.w3-col.m12{width:99.99999%}}
|
||||
@media (min-width:993px){.w3-col.l1{width:8.33333%}.w3-col.l2{width:16.66666%}.w3-col.l3{width:24.99999%}.w3-col.l4{width:33.33333%}
|
||||
.w3-col.l5{width:41.66666%}.w3-col.l6{width:49.99999%}.w3-col.l7{width:58.33333%}.w3-col.l8{width:66.66666%}
|
||||
.w3-col.l9{width:74.99999%}.w3-col.l10{width:83.33333%}.w3-col.l11{width:91.66666%}.w3-col.l12{width:99.99999%}}
|
||||
.w3-rest{overflow:hidden}.w3-stretch{margin-left:-16px;margin-right:-16px}
|
||||
.w3-content,.w3-auto{margin-left:auto;margin-right:auto}.w3-content{max-width:980px}.w3-auto{max-width:1140px}
|
||||
.w3-cell-row{display:table;width:100%}.w3-cell{display:table-cell}
|
||||
.w3-cell-top{vertical-align:top}.w3-cell-middle{vertical-align:middle}.w3-cell-bottom{vertical-align:bottom}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@media (min-width:993px){.w3-modal-content{width:900px}.w3-hide-large{display:none!important}.w3-sidebar.w3-collapse{display:block!important}}
|
||||
@media (max-width:992px) and (min-width:601px){.w3-hide-medium{display:none!important}}
|
||||
@media (max-width:992px){.w3-sidebar.w3-collapse{display:none}.w3-main{margin-left:0!important;margin-right:0!important}.w3-auto{max-width:100%}}
|
||||
.w3-top,.w3-bottom{position:fixed;width:100%;z-index:1}.w3-top{top:0}.w3-bottom{bottom:0}
|
||||
.w3-overlay{position:fixed;display:none;width:100%;height:100%;top:0;left:0;right:0;bottom:0;background-color:rgba(0,0,0,0.5);z-index:2}
|
||||
.w3-display-topleft{position:absolute;left:0;top:0}.w3-display-topright{position:absolute;right:0;top:0}
|
||||
.w3-display-bottomleft{position:absolute;left:0;bottom:0}.w3-display-bottomright{position:absolute;right:0;bottom:0}
|
||||
.w3-display-middle{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%)}
|
||||
.w3-display-left{position:absolute;top:50%;left:0%;transform:translate(0%,-50%);-ms-transform:translate(-0%,-50%)}
|
||||
.w3-display-right{position:absolute;top:50%;right:0%;transform:translate(0%,-50%);-ms-transform:translate(0%,-50%)}
|
||||
.w3-display-topmiddle{position:absolute;left:50%;top:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-bottommiddle{position:absolute;left:50%;bottom:0;transform:translate(-50%,0%);-ms-transform:translate(-50%,0%)}
|
||||
.w3-display-container:hover .w3-display-hover{display:block}.w3-display-container:hover span.w3-display-hover{display:inline-block}.w3-display-hover{display:none}
|
||||
.w3-display-position{position:absolute}
|
||||
.w3-circle{border-radius:50%}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
.w3-card,.w3-card-2{box-shadow:0 2px 5px 0 rgba(0,0,0,0.16),0 2px 10px 0 rgba(0,0,0,0.12)}
|
||||
.w3-card-4,.w3-hover-shadow:hover{box-shadow:0 4px 10px 0 rgba(0,0,0,0.2),0 4px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-spin{animation:w3-spin 2s infinite linear}@keyframes w3-spin{0%{transform:rotate(0deg)}100%{transform:rotate(359deg)}}
|
||||
.w3-animate-fading{animation:fading 10s infinite}@keyframes fading{0%{opacity:0}50%{opacity:1}100%{opacity:0}}
|
||||
.w3-animate-opacity{animation:opac 0.8s}@keyframes opac{from{opacity:0} to{opacity:1}}
|
||||
.w3-animate-top{position:relative;animation:animatetop 0.4s}@keyframes animatetop{from{top:-300px;opacity:0} to{top:0;opacity:1}}
|
||||
.w3-animate-left{position:relative;animation:animateleft 0.4s}@keyframes animateleft{from{left:-300px;opacity:0} to{left:0;opacity:1}}
|
||||
.w3-animate-right{position:relative;animation:animateright 0.4s}@keyframes animateright{from{right:-300px;opacity:0} to{right:0;opacity:1}}
|
||||
.w3-animate-bottom{position:relative;animation:animatebottom 0.4s}@keyframes animatebottom{from{bottom:-300px;opacity:0} to{bottom:0;opacity:1}}
|
||||
.w3-animate-zoom {animation:animatezoom 0.6s}@keyframes animatezoom{from{transform:scale(0)} to{transform:scale(1)}}
|
||||
.w3-animate-input{transition:width 0.4s ease-in-out}.w3-animate-input:focus{width:100%!important}
|
||||
.w3-opacity,.w3-hover-opacity:hover{opacity:0.60}.w3-opacity-off,.w3-hover-opacity-off:hover{opacity:1}
|
||||
.w3-opacity-max{opacity:0.25}.w3-opacity-min{opacity:0.75}
|
||||
.w3-greyscale-max,.w3-grayscale-max,.w3-hover-greyscale:hover,.w3-hover-grayscale:hover{filter:grayscale(100%)}
|
||||
.w3-greyscale,.w3-grayscale{filter:grayscale(75%)}.w3-greyscale-min,.w3-grayscale-min{filter:grayscale(50%)}
|
||||
.w3-sepia{filter:sepia(75%)}.w3-sepia-max,.w3-hover-sepia:hover{filter:sepia(100%)}.w3-sepia-min{filter:sepia(50%)}
|
||||
.w3-tiny{font-size:10px!important}.w3-small{font-size:12px!important}.w3-medium{font-size:15px!important}.w3-large{font-size:18px!important}
|
||||
.w3-xlarge{font-size:24px!important}.w3-xxlarge{font-size:36px!important}.w3-xxxlarge{font-size:48px!important}.w3-jumbo{font-size:64px!important}
|
||||
.w3-left-align{text-align:left!important}.w3-right-align{text-align:right!important}.w3-justify{text-align:justify!important}.w3-center{text-align:center!important}
|
||||
.w3-border-0{border:0!important}.w3-border{border:1px solid #ccc!important}
|
||||
.w3-border-top{border-top:1px solid #ccc!important}.w3-border-bottom{border-bottom:1px solid #ccc!important}
|
||||
.w3-border-left{border-left:1px solid #ccc!important}.w3-border-right{border-right:1px solid #ccc!important}
|
||||
.w3-topbar{border-top:6px solid #ccc!important}.w3-bottombar{border-bottom:6px solid #ccc!important}
|
||||
.w3-leftbar{border-left:6px solid #ccc!important}.w3-rightbar{border-right:6px solid #ccc!important}
|
||||
.w3-section,.w3-code{margin-top:16px!important;margin-bottom:16px!important}
|
||||
.w3-margin{margin:16px!important}.w3-margin-top{margin-top:16px!important}.w3-margin-bottom{margin-bottom:16px!important}
|
||||
.w3-margin-left{margin-left:16px!important}.w3-margin-right{margin-right:16px!important}
|
||||
.w3-padding-small{padding:4px 8px!important}.w3-padding{padding:8px 16px!important}.w3-padding-large{padding:12px 24px!important}
|
||||
.w3-padding-16{padding-top:16px!important;padding-bottom:16px!important}.w3-padding-24{padding-top:24px!important;padding-bottom:24px!important}
|
||||
.w3-padding-32{padding-top:32px!important;padding-bottom:32px!important}.w3-padding-48{padding-top:48px!important;padding-bottom:48px!important}
|
||||
.w3-padding-64{padding-top:64px!important;padding-bottom:64px!important}
|
||||
.w3-padding-top-64{padding-top:64px!important}.w3-padding-top-48{padding-top:48px!important}
|
||||
.w3-padding-top-32{padding-top:32px!important}.w3-padding-top-24{padding-top:24px!important}
|
||||
.w3-left{float:left!important}.w3-right{float:right!important}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
.w3-blue-grey,.w3-hover-blue-grey:hover,.w3-blue-gray,.w3-hover-blue-gray:hover{color:#fff!important;background-color:#607d8b!important}
|
||||
.w3-green,.w3-hover-green:hover{color:#fff!important;background-color:#4CAF50!important}
|
||||
.w3-light-green,.w3-hover-light-green:hover{color:#000!important;background-color:#8bc34a!important}
|
||||
.w3-indigo,.w3-hover-indigo:hover{color:#fff!important;background-color:#3f51b5!important}
|
||||
.w3-khaki,.w3-hover-khaki:hover{color:#000!important;background-color:#f0e68c!important}
|
||||
.w3-lime,.w3-hover-lime:hover{color:#000!important;background-color:#cddc39!important}
|
||||
.w3-orange,.w3-hover-orange:hover{color:#000!important;background-color:#ff9800!important}
|
||||
.w3-deep-orange,.w3-hover-deep-orange:hover{color:#fff!important;background-color:#ff5722!important}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
.w3-pale-blue,.w3-hover-pale-blue:hover{color:#000!important;background-color:#ddffff!important}
|
||||
.w3-text-amber,.w3-hover-text-amber:hover{color:#ffc107!important}
|
||||
.w3-text-aqua,.w3-hover-text-aqua:hover{color:#00ffff!important}
|
||||
.w3-text-blue,.w3-hover-text-blue:hover{color:#2196F3!important}
|
||||
.w3-text-light-blue,.w3-hover-text-light-blue:hover{color:#87CEEB!important}
|
||||
.w3-text-brown,.w3-hover-text-brown:hover{color:#795548!important}
|
||||
.w3-text-cyan,.w3-hover-text-cyan:hover{color:#00bcd4!important}
|
||||
.w3-text-blue-grey,.w3-hover-text-blue-grey:hover,.w3-text-blue-gray,.w3-hover-text-blue-gray:hover{color:#607d8b!important}
|
||||
.w3-text-green,.w3-hover-text-green:hover{color:#4CAF50!important}
|
||||
.w3-text-light-green,.w3-hover-text-light-green:hover{color:#8bc34a!important}
|
||||
.w3-text-indigo,.w3-hover-text-indigo:hover{color:#3f51b5!important}
|
||||
.w3-text-khaki,.w3-hover-text-khaki:hover{color:#b4aa50!important}
|
||||
.w3-text-lime,.w3-hover-text-lime:hover{color:#cddc39!important}
|
||||
.w3-text-orange,.w3-hover-text-orange:hover{color:#ff9800!important}
|
||||
.w3-text-deep-orange,.w3-hover-text-deep-orange:hover{color:#ff5722!important}
|
||||
.w3-text-pink,.w3-hover-text-pink:hover{color:#e91e63!important}
|
||||
.w3-text-purple,.w3-hover-text-purple:hover{color:#9c27b0!important}
|
||||
.w3-text-deep-purple,.w3-hover-text-deep-purple:hover{color:#673ab7!important}
|
||||
.w3-text-red,.w3-hover-text-red:hover{color:#f44336!important}
|
||||
.w3-text-sand,.w3-hover-text-sand:hover{color:#fdf5e6!important}
|
||||
.w3-text-teal,.w3-hover-text-teal:hover{color:#009688!important}
|
||||
.w3-text-yellow,.w3-hover-text-yellow:hover{color:#d2be0e!important}
|
||||
.w3-text-white,.w3-hover-text-white:hover{color:#fff!important}
|
||||
.w3-text-black,.w3-hover-text-black:hover{color:#000!important}
|
||||
.w3-text-grey,.w3-hover-text-grey:hover,.w3-text-gray,.w3-hover-text-gray:hover{color:#757575!important}
|
||||
.w3-text-light-grey,.w3-hover-text-light-grey:hover,.w3-text-light-gray,.w3-hover-text-light-gray:hover{color:#f1f1f1!important}
|
||||
.w3-text-dark-grey,.w3-hover-text-dark-grey:hover,.w3-text-dark-gray,.w3-hover-text-dark-gray:hover{color:#3a3a3a!important}
|
||||
.w3-border-amber,.w3-hover-border-amber:hover{border-color:#ffc107!important}
|
||||
.w3-border-aqua,.w3-hover-border-aqua:hover{border-color:#00ffff!important}
|
||||
.w3-border-blue,.w3-hover-border-blue:hover{border-color:#2196F3!important}
|
||||
.w3-border-light-blue,.w3-hover-border-light-blue:hover{border-color:#87CEEB!important}
|
||||
.w3-border-brown,.w3-hover-border-brown:hover{border-color:#795548!important}
|
||||
.w3-border-cyan,.w3-hover-border-cyan:hover{border-color:#00bcd4!important}
|
||||
.w3-border-blue-grey,.w3-hover-border-blue-grey:hover,.w3-border-blue-gray,.w3-hover-border-blue-gray:hover{border-color:#607d8b!important}
|
||||
.w3-border-green,.w3-hover-border-green:hover{border-color:#4CAF50!important}
|
||||
.w3-border-light-green,.w3-hover-border-light-green:hover{border-color:#8bc34a!important}
|
||||
.w3-border-indigo,.w3-hover-border-indigo:hover{border-color:#3f51b5!important}
|
||||
.w3-border-khaki,.w3-hover-border-khaki:hover{border-color:#f0e68c!important}
|
||||
.w3-border-lime,.w3-hover-border-lime:hover{border-color:#cddc39!important}
|
||||
.w3-border-orange,.w3-hover-border-orange:hover{border-color:#ff9800!important}
|
||||
.w3-border-deep-orange,.w3-hover-border-deep-orange:hover{border-color:#ff5722!important}
|
||||
.w3-border-pink,.w3-hover-border-pink:hover{border-color:#e91e63!important}
|
||||
.w3-border-purple,.w3-hover-border-purple:hover{border-color:#9c27b0!important}
|
||||
.w3-border-deep-purple,.w3-hover-border-deep-purple:hover{border-color:#673ab7!important}
|
||||
.w3-border-red,.w3-hover-border-red:hover{border-color:#f44336!important}
|
||||
.w3-border-sand,.w3-hover-border-sand:hover{border-color:#fdf5e6!important}
|
||||
.w3-border-teal,.w3-hover-border-teal:hover{border-color:#009688!important}
|
||||
.w3-border-yellow,.w3-hover-border-yellow:hover{border-color:#ffeb3b!important}
|
||||
.w3-border-white,.w3-hover-border-white:hover{border-color:#fff!important}
|
||||
.w3-border-black,.w3-hover-border-black:hover{border-color:#000!important}
|
||||
.w3-border-grey,.w3-hover-border-grey:hover,.w3-border-gray,.w3-hover-border-gray:hover{border-color:#9e9e9e!important}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
42
apps/issues/lit-all.min.js
vendored
42
apps/journal/lit-all.min.js
vendored
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🚪",
|
||||
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
|
||||
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
|
||||
}
|
||||
|
@ -1,7 +1,9 @@
|
||||
async function main() {
|
||||
let host = core.url.match(/.*\/\/(.*?)\//)[1];
|
||||
let id = (await ssb.getServerIdentity()).substring(1);
|
||||
let room = `net:${host}:${ssb.port}~shs:${id}:SSB+Room+SK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
print(core.url);
|
||||
let host = core.url.match(/.*?\/\/([^:/]*)/)[1];
|
||||
let port = await ssb.port();
|
||||
let id = (await ssb.getServerIdentity()).substring(1).split('.')[0];
|
||||
let room = `net:${host}:${port}~shs:${id}:SSB+Room+PSK3TLYC2T86EHQCUHBUHASCASE18JBV24=`;
|
||||
await app.setDocument(`
|
||||
<body style="color: #fff">
|
||||
<h1>Server</h1>
|
||||
|
42
apps/sneaker/lit-all.min.js
vendored
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🦀",
|
||||
"previous": "&m5ZrkzoyAjv7AY6AukzVoWIyUFUL97uuDDWCtxCloAU=.sha256"
|
||||
"previous": "&Rn4Eg5ev5qhrYRnwxPB0DiEwO7VdGMDGp7tL/W7bRZo=.sha256"
|
||||
}
|
||||
|
@ -106,6 +106,15 @@ tfrpc.register(async function sync() {
|
||||
tfrpc.register(async function url() {
|
||||
return core.url;
|
||||
});
|
||||
tfrpc.register(async function globalSettingsGet(key) {
|
||||
return core.globalSettingsGet(key);
|
||||
});
|
||||
tfrpc.register(async function globalSettingsSet(key, value) {
|
||||
return core.globalSettingsSet(key, value);
|
||||
});
|
||||
tfrpc.register(function isAdministrator() {
|
||||
return core.user?.credentials?.permissions?.administration;
|
||||
});
|
||||
|
||||
core.register('onBroadcastsChanged', async function () {
|
||||
await tfrpc.rpc.set('broadcasts', await ssb.getBroadcasts());
|
||||
|
@ -14,23 +14,8 @@ function get_emojis() {
|
||||
});
|
||||
}
|
||||
|
||||
async function get_recent(author) {
|
||||
let recent = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT DISTINCT content ->> '$.vote.expression' AS value
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
content ->> '$.type' = 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
`,
|
||||
[author]
|
||||
);
|
||||
return recent.map((x) => x.value);
|
||||
}
|
||||
|
||||
export async function picker(callback, anchor, author) {
|
||||
export async function picker(callback, anchor, author, recent) {
|
||||
let json = await get_emojis();
|
||||
let recent = await get_recent(author);
|
||||
|
||||
let div = document.createElement('div');
|
||||
div.id = 'emoji_picker';
|
||||
|
42
apps/ssb/lit-all.min.js
vendored
@ -11,6 +11,7 @@ class TfElement extends LitElement {
|
||||
broadcasts: {type: Array},
|
||||
connections: {type: Array},
|
||||
loading: {type: Boolean},
|
||||
loading_about: {type: Number},
|
||||
loaded: {type: Boolean},
|
||||
following: {type: Array},
|
||||
users: {type: Object},
|
||||
@ -20,6 +21,8 @@ class TfElement extends LitElement {
|
||||
channels_latest: {type: Object},
|
||||
guest: {type: Boolean},
|
||||
url: {type: String},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
@ -35,11 +38,13 @@ class TfElement extends LitElement {
|
||||
this.following = [];
|
||||
this.users = {};
|
||||
this.loaded = false;
|
||||
this.loading_about = 0;
|
||||
this.channels = [];
|
||||
this.channels_unread = {};
|
||||
this.channels_latest = {};
|
||||
this.loading_latest = 0;
|
||||
this.loading_latest_scheduled = 0;
|
||||
this.recent_reactions = [];
|
||||
tfrpc.rpc.getBroadcasts().then((b) => {
|
||||
self.broadcasts = b || [];
|
||||
});
|
||||
@ -123,7 +128,13 @@ class TfElement extends LitElement {
|
||||
}
|
||||
|
||||
next_channel(delta) {
|
||||
let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
|
||||
let channel_names = [
|
||||
'',
|
||||
'@',
|
||||
'🔐',
|
||||
'👍',
|
||||
...this.channels.map((x) => '#' + x),
|
||||
];
|
||||
let index = channel_names.indexOf(this.hash.substring(1));
|
||||
index = index != -1 ? index + delta : 0;
|
||||
tfrpc.rpc.setHash(
|
||||
@ -148,8 +159,9 @@ class TfElement extends LitElement {
|
||||
}
|
||||
|
||||
async fetch_about(following, users) {
|
||||
this.loading_about++;
|
||||
let ids = Object.keys(following).sort();
|
||||
const k_cache_version = 1;
|
||||
const k_cache_version = 3;
|
||||
let cache = await tfrpc.rpc.databaseGet('about');
|
||||
let original_cache = cache;
|
||||
cache = cache ? JSON.parse(cache) : {};
|
||||
@ -157,81 +169,86 @@ class TfElement extends LitElement {
|
||||
cache = {
|
||||
version: k_cache_version,
|
||||
about: {},
|
||||
last_row_id: 0,
|
||||
};
|
||||
}
|
||||
let max_row_id = (
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT MAX(rowid) AS max_row_id FROM messages
|
||||
`,
|
||||
[]
|
||||
)
|
||||
)[0].max_row_id;
|
||||
|
||||
let ids_out_of_date = ids.filter(
|
||||
(x) =>
|
||||
(users[x]?.seq && !cache.about[x]?.seq) ||
|
||||
(users[x]?.seq && users[x]?.seq > cache.about[x].seq)
|
||||
);
|
||||
|
||||
for (let id of Object.keys(cache.about)) {
|
||||
if (ids.indexOf(id) == -1) {
|
||||
delete cache.about[id];
|
||||
} else {
|
||||
users[id] = Object.assign(cache.about[id], users[id] || {});
|
||||
}
|
||||
}
|
||||
|
||||
let abouts = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT
|
||||
messages.author, json(messages.content) AS content, messages.sequence
|
||||
FROM
|
||||
messages,
|
||||
json_each(?1) AS following
|
||||
WHERE
|
||||
messages.author = following.value AND
|
||||
messages.rowid > ?3 AND
|
||||
messages.rowid <= ?4 AND
|
||||
json_extract(messages.content, '$.type') = 'about'
|
||||
UNION
|
||||
SELECT
|
||||
messages.author, json(messages.content) AS content, messages.sequence
|
||||
FROM
|
||||
messages,
|
||||
json_each(?2) AS following
|
||||
WHERE
|
||||
messages.author = following.value AND
|
||||
messages.rowid <= ?4 AND
|
||||
json_extract(messages.content, '$.type') = 'about'
|
||||
ORDER BY messages.author, messages.sequence
|
||||
`,
|
||||
[
|
||||
JSON.stringify(ids.filter((id) => cache.about[id])),
|
||||
JSON.stringify(ids.filter((id) => !cache.about[id])),
|
||||
cache.last_row_id,
|
||||
max_row_id,
|
||||
]
|
||||
console.log(
|
||||
'loading about for',
|
||||
ids.length,
|
||||
'accounts',
|
||||
ids_out_of_date.length,
|
||||
'out of date'
|
||||
);
|
||||
for (let about of abouts) {
|
||||
let content = JSON.parse(about.content);
|
||||
if (content.about === about.author) {
|
||||
delete content.type;
|
||||
delete content.about;
|
||||
cache.about[about.author] = Object.assign(
|
||||
cache.about[about.author] || {},
|
||||
content
|
||||
if (ids_out_of_date.length) {
|
||||
try {
|
||||
let rows = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT all_abouts.author, json(json_group_object(all_abouts.key, all_abouts.value)) AS about
|
||||
FROM (
|
||||
SELECT
|
||||
messages.author,
|
||||
fields.key,
|
||||
RANK() OVER (PARTITION BY messages.author, fields.key ORDER BY messages.sequence DESC) AS rank,
|
||||
fields.value
|
||||
FROM messages JOIN json_each(messages.content) AS fields
|
||||
WHERE
|
||||
messages.content ->> '$.type' = 'about' AND
|
||||
messages.content ->> '$.about' = messages.author AND
|
||||
NOT fields.key IN ('about', 'type')) all_abouts
|
||||
JOIN json_each(?) AS following ON all_abouts.author = following.value
|
||||
WHERE rank = 1
|
||||
GROUP BY all_abouts.author
|
||||
`,
|
||||
[JSON.stringify(ids_out_of_date)]
|
||||
);
|
||||
users = users || {};
|
||||
for (let row of rows) {
|
||||
users[row.author] = Object.assign(
|
||||
users[row.author] || {},
|
||||
JSON.parse(row.about)
|
||||
);
|
||||
cache.about[row.author] = Object.assign(
|
||||
{seq: users[row.author].seq},
|
||||
JSON.parse(row.about)
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
}
|
||||
cache.last_row_id = max_row_id;
|
||||
|
||||
for (let id of ids_out_of_date) {
|
||||
if (!cache.about[id]?.seq) {
|
||||
cache.about[id] = Object.assign(cache.about[id] ?? {}, {
|
||||
seq: users[id]?.seq ?? 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.loading_about--;
|
||||
|
||||
let new_cache = JSON.stringify(cache);
|
||||
if (new_cache !== original_cache) {
|
||||
if (new_cache != original_cache) {
|
||||
let start_time = new Date();
|
||||
tfrpc.rpc.databaseSet('about', new_cache).then(function () {
|
||||
console.log('saving about took', (new Date() - start_time) / 1000);
|
||||
});
|
||||
}
|
||||
users = users || {};
|
||||
for (let id of Object.keys(cache.about)) {
|
||||
users[id] = Object.assign(
|
||||
{follow_depth: following[id]?.d},
|
||||
users[id] || {},
|
||||
cache.about[id]
|
||||
);
|
||||
}
|
||||
|
||||
return Object.assign({}, users);
|
||||
}
|
||||
|
||||
@ -291,11 +308,7 @@ class TfElement extends LitElement {
|
||||
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
|
||||
}
|
||||
for (let i = cache.range[0]; i >= 0; i -= k_chunk_size) {
|
||||
ranges.push([
|
||||
Math.max(i - k_chunk_size, 0),
|
||||
Math.min(cache.range[0], i + k_chunk_size),
|
||||
false,
|
||||
]);
|
||||
ranges.push([Math.max(i - k_chunk_size, 0), i, false]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < latest; i += k_chunk_size) {
|
||||
@ -311,7 +324,7 @@ class TfElement extends LitElement {
|
||||
messages.rowid > ?1 AND
|
||||
messages.rowid <= ?2 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY sequence DESC
|
||||
ORDER BY messages.rowid DESC
|
||||
`,
|
||||
[range[0], range[1]]
|
||||
);
|
||||
@ -334,53 +347,105 @@ class TfElement extends LitElement {
|
||||
JSON.stringify(cache)
|
||||
);
|
||||
}
|
||||
return cache.latest;
|
||||
return [cache.latest, cache.messages];
|
||||
}
|
||||
|
||||
async query_timed(sql, args) {
|
||||
let start = new Date();
|
||||
let result = await tfrpc.rpc.query(sql, args);
|
||||
let end = new Date();
|
||||
console.log((end - start) / 1000, sql);
|
||||
return result;
|
||||
}
|
||||
|
||||
async load_channels_latest(following) {
|
||||
let start_time = new Date();
|
||||
let latest_private = this.get_latest_private(following);
|
||||
let channels = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
UNION
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
UNION
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
this.whoami,
|
||||
]
|
||||
);
|
||||
this.channels_latest = Object.fromEntries(
|
||||
channels.map((x) => [x.channel, x.rowid])
|
||||
);
|
||||
const k_args = [
|
||||
JSON.stringify(this.channels),
|
||||
JSON.stringify(following),
|
||||
'"' + this.whoami.replace('"', '""') + '"',
|
||||
this.whoami,
|
||||
];
|
||||
let channels = (
|
||||
await Promise.all([
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?1) AS channels ON messages.content ->> 'channel' = channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT channels.value AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN messages_refs ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS channels ON messages_refs.ref = '#' || channels.value
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
GROUP by channel
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'post' AND
|
||||
messages.content ->> 'root' IS NULL AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '@' AS channel, MAX(messages.rowid) AS rowid FROM messages_fts(?3)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
this.query_timed(
|
||||
`
|
||||
SELECT '👍' AS channel, MAX(messages.rowid) AS rowid FROM messages
|
||||
JOIN json_each(?2) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
messages.author != ?4
|
||||
`,
|
||||
k_args
|
||||
),
|
||||
])
|
||||
).flat();
|
||||
let latest = {};
|
||||
for (let row of channels) {
|
||||
if (!latest[row.channel]) {
|
||||
latest[row.channel] = row.rowid;
|
||||
} else {
|
||||
latest[row.channel] = Math.max(row.rowid, latest[row.channel]);
|
||||
}
|
||||
}
|
||||
this.channels_latest = latest;
|
||||
console.log('channels took', (new Date() - start_time) / 1000.0);
|
||||
let self = this;
|
||||
start_time = new Date();
|
||||
latest_private.then(function (latest) {
|
||||
self.channels_latest = Object.assign({}, self.channels_latest, {
|
||||
'🔐': latest,
|
||||
'🔐': latest[0],
|
||||
});
|
||||
console.log('private took', (new Date() - start_time) / 1000.0);
|
||||
self.private_messages = latest[1];
|
||||
});
|
||||
}
|
||||
|
||||
@ -409,43 +474,58 @@ class TfElement extends LitElement {
|
||||
[JSON.stringify(Object.keys(users))]
|
||||
);
|
||||
for (let row of info) {
|
||||
users[row.author].seq = row.max_seq;
|
||||
users[row.author].ts = row.max_ts;
|
||||
users[row.author] = Object.assign(users[row.author], {
|
||||
seq: row.max_sequence,
|
||||
ts: row.max_ts,
|
||||
});
|
||||
}
|
||||
return users;
|
||||
}
|
||||
|
||||
async load_recent_reactions() {
|
||||
this.recent_reactions = (
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT DISTINCT content ->> '$.vote.expression' AS value
|
||||
FROM messages
|
||||
WHERE author = ? AND
|
||||
content ->> '$.type' = 'vote'
|
||||
ORDER BY timestamp DESC LIMIT 10
|
||||
`,
|
||||
[this.whoami]
|
||||
)
|
||||
).map((x) => x.value);
|
||||
}
|
||||
|
||||
async load() {
|
||||
this.loading_latest = true;
|
||||
try {
|
||||
let start_time = new Date();
|
||||
let whoami = this.whoami;
|
||||
let following = await tfrpc.rpc.following([whoami], 2);
|
||||
let old_users = this.users ?? {};
|
||||
let users = {};
|
||||
let by_count = [];
|
||||
for (let [id, v] of Object.entries(following)) {
|
||||
users[id] = {
|
||||
following: v.of,
|
||||
blocking: v.ob,
|
||||
followed: v.if,
|
||||
blocked: v.ib,
|
||||
};
|
||||
users[id] = Object.assign(
|
||||
{
|
||||
following: v.of,
|
||||
blocking: v.ob,
|
||||
followed: v.if,
|
||||
blocked: v.ib,
|
||||
follow_depth: following[id]?.d,
|
||||
},
|
||||
old_users[id]
|
||||
);
|
||||
by_count.push({count: v.of, id: id});
|
||||
}
|
||||
let reactions = this.load_recent_reactions();
|
||||
this.load_channels_latest(Object.keys(following));
|
||||
this.channels_unread = JSON.parse(
|
||||
(await tfrpc.rpc.databaseGet('unread')) ?? '{}'
|
||||
);
|
||||
this.following = Object.keys(following);
|
||||
let about_start_time = new Date();
|
||||
users = await this.fetch_about(following, users);
|
||||
console.log(
|
||||
'about took',
|
||||
(new Date() - about_start_time) / 1000.0,
|
||||
'seconds for',
|
||||
Object.keys(users).length,
|
||||
'users'
|
||||
);
|
||||
start_time = new Date();
|
||||
users = await this.fetch_user_info(users);
|
||||
console.log(
|
||||
@ -454,9 +534,22 @@ class TfElement extends LitElement {
|
||||
'seconds'
|
||||
);
|
||||
this.users = users;
|
||||
|
||||
let self = this;
|
||||
this.fetch_about(following, users).then(function (result) {
|
||||
self.users = result;
|
||||
console.log(
|
||||
'about took',
|
||||
(new Date() - about_start_time) / 1000.0,
|
||||
'seconds for',
|
||||
Object.keys(users).length,
|
||||
'users'
|
||||
);
|
||||
});
|
||||
console.log(
|
||||
`load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}`
|
||||
);
|
||||
await reactions;
|
||||
this.whoami = whoami;
|
||||
this.loaded = whoami;
|
||||
} finally {
|
||||
@ -507,12 +600,14 @@ class TfElement extends LitElement {
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
hash=${this.hash}
|
||||
?loading=${this.loading}
|
||||
?loading=${this.loading || this.loading_about != 0}
|
||||
.channels=${this.channels}
|
||||
.channels_latest=${this.channels_latest}
|
||||
.channels_unread=${this.channels_unread}
|
||||
@channelsetunread=${this.channel_set_unread}
|
||||
.connections=${this.connections}
|
||||
.private_messages=${this.private_messages}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-tab-news>
|
||||
`;
|
||||
} else if (this.tab === 'connections') {
|
||||
|
@ -255,10 +255,12 @@ class TfComposeElement extends LitElement {
|
||||
let self = this;
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = function (event) {
|
||||
input.addEventListener('change', function (event) {
|
||||
input.parentNode.removeChild(input);
|
||||
let file = event.target.files[0];
|
||||
self.add_file(file);
|
||||
};
|
||||
});
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
@ -357,7 +359,7 @@ class TfComposeElement extends LitElement {
|
||||
remove_mention(id) {
|
||||
let draft = this.get_draft();
|
||||
delete draft.mentions[id];
|
||||
setTimeout(() => this.notify(), 0);
|
||||
setTimeout(() => this.notify(draft), 0);
|
||||
}
|
||||
|
||||
render_mention(mention) {
|
||||
@ -444,12 +446,15 @@ class TfComposeElement extends LitElement {
|
||||
self.apps = await tfrpc.rpc.apps();
|
||||
}
|
||||
if (!this.apps) {
|
||||
return html`<button class="w3-button w3-theme-d1" @click=${attach_app}>
|
||||
return html`<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${attach_app}
|
||||
>
|
||||
Attach App
|
||||
</button>`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => (this.apps = null)}
|
||||
>
|
||||
Discard App
|
||||
@ -470,18 +475,9 @@ class TfComposeElement extends LitElement {
|
||||
if (draft.content_warning !== undefined) {
|
||||
return html`
|
||||
<div class="w3-container w3-padding">
|
||||
<p>
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning(undefined)} checked="checked"></input>
|
||||
<label for="cw">CW</label>
|
||||
</p>
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" id="content_warning" placeholder="Enter a content warning here." @input=${self.input} value=${draft.content_warning}></input>
|
||||
</div>
|
||||
`;
|
||||
} else {
|
||||
return html`
|
||||
<input type="checkbox" class="w3-check w3-theme-d1" id="cw" @change=${() => self.set_content_warning('')}></input>
|
||||
<label for="cw">CW</label>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -522,7 +518,7 @@ class TfComposeElement extends LitElement {
|
||||
return html`
|
||||
<div style="display: flex; flex-direction: row; width: 100%">
|
||||
<label for="encrypt_to">🔐 To:</label>
|
||||
<input type="text" id="encrypt_to" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
|
||||
<input type="text" id="encrypt_to" class="w3-input w3-theme-d1 w3-border" style="display: flex; flex: 1 1" @input=${this.update_encrypt}></input>
|
||||
<button class="w3-button w3-theme-d1" @click=${() => this.set_encrypt(undefined)}>🚮</button>
|
||||
</div>
|
||||
<ul>
|
||||
@ -544,6 +540,31 @@ class TfComposeElement extends LitElement {
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
toggle_menu(event) {
|
||||
event.srcElement.parentNode
|
||||
.querySelector('.w3-dropdown-content')
|
||||
.classList.toggle('w3-show');
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._click_callback = this.document_click.bind(this);
|
||||
document.body.addEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.removeEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
document_click(event) {
|
||||
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||
let target = event.target;
|
||||
if (content && !content.contains(target)) {
|
||||
content.classList.remove('w3-show');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
let draft = self.get_draft();
|
||||
@ -557,7 +578,7 @@ class TfComposeElement extends LitElement {
|
||||
draft.encrypt_to !== undefined
|
||||
? undefined
|
||||
: html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => this.set_encrypt([])}
|
||||
>
|
||||
🔐
|
||||
@ -612,13 +633,43 @@ class TfComposeElement extends LitElement {
|
||||
>
|
||||
Submit
|
||||
</button>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.attach}>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button class="w3-button w3-theme-d1" @click=${this.discard}>
|
||||
Discard
|
||||
</button>
|
||||
<div class="w3-dropdown-click">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
⚙️
|
||||
</button>
|
||||
<div class="w3-dropdown-content w3-bar-block">
|
||||
${this.get_draft().content_warning === undefined
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning('')}
|
||||
>
|
||||
Add Content Warning
|
||||
</button>
|
||||
`
|
||||
: html`
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${() => self.set_content_warning(undefined)}
|
||||
>
|
||||
Remove Content Warning
|
||||
</button>
|
||||
`}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.attach}
|
||||
>
|
||||
Attach
|
||||
</button>
|
||||
${this.render_attach_app_button()} ${encrypt}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-theme-d1"
|
||||
@click=${this.discard}
|
||||
>
|
||||
Discard
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
`;
|
||||
|
@ -1,4 +1,11 @@
|
||||
import {LitElement, html, repeat, render, unsafeHTML} from './lit-all.min.js';
|
||||
import {
|
||||
LitElement,
|
||||
css,
|
||||
html,
|
||||
repeat,
|
||||
render,
|
||||
unsafeHTML,
|
||||
} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import * as emojis from './emojis.js';
|
||||
@ -16,6 +23,7 @@ class TfMessageElement extends LitElement {
|
||||
expanded: {type: Object},
|
||||
channel: {type: String},
|
||||
channel_unread: {type: Number},
|
||||
recent_reactions: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
@ -31,6 +39,26 @@ class TfMessageElement extends LitElement {
|
||||
this.format = 'message';
|
||||
this.expanded = {};
|
||||
this.channel_unread = -1;
|
||||
this.recent_reactions = [];
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
super.connectedCallback();
|
||||
this._click_callback = this.document_click.bind(this);
|
||||
document.body.addEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
disconnectedCallback() {
|
||||
super.disconnectedCallback();
|
||||
document.body.removeEventListener('mouseup', this._click_callback);
|
||||
}
|
||||
|
||||
document_click(event) {
|
||||
let content = this.renderRoot.querySelector('.w3-dropdown-content');
|
||||
let target = event.target;
|
||||
if (content && !content.contains(target)) {
|
||||
content.classList.remove('w3-show');
|
||||
}
|
||||
}
|
||||
|
||||
show_reply() {
|
||||
@ -65,20 +93,27 @@ class TfMessageElement extends LitElement {
|
||||
|
||||
render_votes() {
|
||||
function normalize_expression(expression) {
|
||||
if (expression === 'Like' || !expression) {
|
||||
return '👍';
|
||||
} else if (expression === 'Unlike') {
|
||||
if (
|
||||
expression === 'Unlike' ||
|
||||
expression === 'unlike' ||
|
||||
expression == 'undig'
|
||||
) {
|
||||
return '👎';
|
||||
} else if (expression === 'heart') {
|
||||
return '❤️';
|
||||
} else if (
|
||||
(expression ?? '').split('').every((x) => x.charCodeAt(0) < 256)
|
||||
) {
|
||||
return '👍';
|
||||
} else {
|
||||
return expression;
|
||||
}
|
||||
}
|
||||
if (this.message?.votes?.length) {
|
||||
return html` <div class="w3-container">
|
||||
return html` <footer class="w3-container">
|
||||
<div
|
||||
class="w3-button w3-bar w3-padding-small"
|
||||
class="w3-button w3-bar"
|
||||
style="padding: 0"
|
||||
@click=${this.show_reactions}
|
||||
>
|
||||
${(this.message.votes || []).map(
|
||||
@ -93,7 +128,7 @@ class TfMessageElement extends LitElement {
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>`;
|
||||
</footer>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,7 +171,12 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
|
||||
react(event) {
|
||||
emojis.picker((x) => this.vote(x), null, this.whoami);
|
||||
emojis.picker(
|
||||
(x) => this.vote(x),
|
||||
null,
|
||||
this.whoami,
|
||||
this.recent_reactions
|
||||
);
|
||||
}
|
||||
|
||||
show_image(link) {
|
||||
@ -270,53 +310,69 @@ class TfMessageElement extends LitElement {
|
||||
return total;
|
||||
}
|
||||
|
||||
expanded_key() {
|
||||
return this.message?.id || this.messages?.map((x) => x.id).join(':');
|
||||
}
|
||||
|
||||
set_expanded(expanded, tag) {
|
||||
let key = this.expanded_key();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('tf-expand', {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
detail: {id: (this.message.id || '') + (tag || ''), expanded: expanded},
|
||||
detail: {id: key + (tag || ''), expanded: expanded},
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
toggle_expanded(tag) {
|
||||
this.set_expanded(
|
||||
!this.expanded[(this.message.id || '') + (tag || '')],
|
||||
tag
|
||||
);
|
||||
let key = this.expanded_key();
|
||||
this.set_expanded(!this.expanded[key + (tag || '')], tag);
|
||||
}
|
||||
|
||||
is_expanded(tag) {
|
||||
let key = this.expanded_key();
|
||||
return this.expanded[key + (tag || '')];
|
||||
}
|
||||
|
||||
render_children() {
|
||||
let self = this;
|
||||
if (this.message.child_messages?.length) {
|
||||
if (!this.expanded[this.message.id]) {
|
||||
return html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => self.set_expanded(true)}
|
||||
>
|
||||
+ ${this.total_child_messages(this.message) + ' More'}
|
||||
</button>`;
|
||||
if (!this.expanded[this.expanded_key()]) {
|
||||
return html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||
style="box-sizing: border-box"
|
||||
@click=${() => self.set_expanded(true)}
|
||||
>
|
||||
+ ${this.total_child_messages(this.message) + ' More'}
|
||||
</button>
|
||||
`;
|
||||
} else {
|
||||
return html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
return html` <div class="w3-container w3-margin-bottom">
|
||||
${repeat(
|
||||
this.message.child_messages || [],
|
||||
(x) => x.id,
|
||||
(x) =>
|
||||
html`<tf-message
|
||||
.message=${x}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-message>`
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||
style="box-sizing: border-box"
|
||||
@click=${() => self.set_expanded(false)}
|
||||
>
|
||||
Collapse</button
|
||||
>${repeat(
|
||||
this.message.child_messages || [],
|
||||
(x) => x.id,
|
||||
(x) =>
|
||||
html`<tf-message
|
||||
.message=${x}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
></tf-message>`
|
||||
)}`;
|
||||
Collapse
|
||||
</button>`;
|
||||
}
|
||||
} else {
|
||||
return undefined;
|
||||
@ -358,7 +414,7 @@ class TfMessageElement extends LitElement {
|
||||
class_background() {
|
||||
return this.message?.decrypted
|
||||
? 'w3-pale-red'
|
||||
: this.message?.rowid >= this.channel_unread
|
||||
: this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||
? 'w3-theme-d2'
|
||||
: 'w3-theme-d4';
|
||||
}
|
||||
@ -371,62 +427,74 @@ class TfMessageElement extends LitElement {
|
||||
return content;
|
||||
}
|
||||
|
||||
render_raw_button() {
|
||||
copy_id(event) {
|
||||
navigator.clipboard.writeText(this.message?.id);
|
||||
}
|
||||
|
||||
toggle_menu(event) {
|
||||
event.srcElement.parentNode
|
||||
.querySelector('.w3-dropdown-content')
|
||||
.classList.toggle('w3-show');
|
||||
}
|
||||
|
||||
render_menu() {
|
||||
let content = this.get_content();
|
||||
let raw_button;
|
||||
switch (this.format) {
|
||||
case 'raw':
|
||||
if (content?.type == 'post' || content?.type == 'blog') {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'md')}
|
||||
>
|
||||
Markdown
|
||||
</button>`;
|
||||
} else {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'message')}
|
||||
>
|
||||
Message
|
||||
</button>`;
|
||||
}
|
||||
break;
|
||||
case 'md':
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'message')}
|
||||
>
|
||||
Message
|
||||
</button>`;
|
||||
break;
|
||||
case 'decrypted':
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'raw')}
|
||||
>
|
||||
Raw
|
||||
</button>`;
|
||||
break;
|
||||
default:
|
||||
if (this.message.decrypted) {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'decrypted')}
|
||||
>
|
||||
Decrypted
|
||||
</button>`;
|
||||
} else {
|
||||
raw_button = html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${() => (this.format = 'raw')}
|
||||
>
|
||||
Raw
|
||||
</button>`;
|
||||
}
|
||||
break;
|
||||
let formats = [['message', 'Message']];
|
||||
if (content?.type == 'post' || content?.type == 'blog') {
|
||||
formats.push(['md', 'Markdown']);
|
||||
}
|
||||
return raw_button;
|
||||
if (this.message?.decrypted) {
|
||||
formats.push(['decrypted', 'Decrypted']);
|
||||
}
|
||||
formats.push(['raw', 'Raw']);
|
||||
return html`
|
||||
<div class="w3-bar-item w3-right">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
%
|
||||
</button>
|
||||
<div
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||
style="right: 48px"
|
||||
>
|
||||
<a
|
||||
target="_top"
|
||||
class="w3-button w3-bar-item"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>View Message</a
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.copy_id}
|
||||
>
|
||||
Copy ID
|
||||
</button>
|
||||
${this.drafts[this.message?.id] === undefined
|
||||
? html`
|
||||
<button class="w3-button w3-bar-item" @click=${this.show_reply}>
|
||||
↩️ Reply
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.react}
|
||||
>
|
||||
👍 React
|
||||
</button>
|
||||
${formats.map(
|
||||
([format, name]) => html`
|
||||
<button
|
||||
class="w3-button w3-bar-item"
|
||||
style=${format == this.format ? 'font-weight: bold' : ''}
|
||||
@click=${() => (this.format = format)}
|
||||
>
|
||||
${name}
|
||||
</button>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render_header() {
|
||||
@ -438,16 +506,15 @@ class TfMessageElement extends LitElement {
|
||||
return html`
|
||||
<header class="w3-bar">
|
||||
<span class="w3-bar-item">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
${this.render_unread_icon()}<tf-user
|
||||
id=${this.message.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
</span>
|
||||
${is_encrypted}
|
||||
<span class="w3-bar-item w3-right">${this.render_raw_button()}</span>
|
||||
<span class="w3-bar-item w3-right" style="text-wrap: nowrap"
|
||||
><a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
||||
>%</a
|
||||
>
|
||||
${new Date(this.message.timestamp).toLocaleString()}</span
|
||||
>
|
||||
${is_encrypted} ${this.render_menu()}
|
||||
<div class="w3-bar-item w3-right" style="text-wrap: nowrap">
|
||||
${new Date(this.message.timestamp).toLocaleString()}
|
||||
</div>
|
||||
</header>
|
||||
`;
|
||||
}
|
||||
@ -495,6 +562,7 @@ class TfMessageElement extends LitElement {
|
||||
.expanded=${self.expanded}
|
||||
channel=${self.channel}
|
||||
channel_unread=${self.channel_unread}
|
||||
.recent_reactions=${self.recent_reactions}
|
||||
></tf-message>
|
||||
`
|
||||
)}
|
||||
@ -506,32 +574,76 @@ class TfMessageElement extends LitElement {
|
||||
let reply =
|
||||
this.drafts[this.message?.id] !== undefined
|
||||
? html`
|
||||
<tf-compose
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
root=${content.root || this.message.id}
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
author=${this.message.author}
|
||||
></tf-compose>
|
||||
<div class="w3-section w3-container">
|
||||
<tf-compose
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
root=${content.root || this.message.id}
|
||||
branch=${this.message.id}
|
||||
.drafts=${this.drafts}
|
||||
@tf-discard=${this.discard_reply}
|
||||
author=${this.message.author}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-compose>
|
||||
</div>
|
||||
`
|
||||
: html`
|
||||
<button class="w3-button w3-theme-d1" @click=${this.show_reply}>
|
||||
Reply
|
||||
</button>
|
||||
`;
|
||||
: undefined;
|
||||
return html`
|
||||
<div class="w3-section w3-container">
|
||||
${reply}
|
||||
<button class="w3-button w3-theme-d1" @click=${this.react}>
|
||||
React
|
||||
</button>
|
||||
${this.render_children()}
|
||||
</div>
|
||||
${reply}
|
||||
<footer>${this.render_children()}</footer>
|
||||
`;
|
||||
}
|
||||
|
||||
content_group_by_author() {
|
||||
let sorted = this.message.messages
|
||||
.map((x) => [
|
||||
x.author,
|
||||
x.content.blocking !== undefined
|
||||
? x.content.blocking
|
||||
? 'is blocking'
|
||||
: 'is no longer blocking'
|
||||
: x.content.following !== undefined
|
||||
? x.content.following
|
||||
? 'is following'
|
||||
: 'is no longer following'
|
||||
: '',
|
||||
x.content.contact,
|
||||
x,
|
||||
])
|
||||
.sort();
|
||||
let result = [];
|
||||
let last;
|
||||
let group;
|
||||
for (let row of sorted) {
|
||||
if (last && last[0] == row[0] && last[1] == row[1]) {
|
||||
group.push(row[2]);
|
||||
} else {
|
||||
if (group) {
|
||||
result.push({author: last[0], action: last[1], users: group});
|
||||
}
|
||||
last = row;
|
||||
group = [row[2]];
|
||||
}
|
||||
}
|
||||
if (group) {
|
||||
result.push({author: last[0], action: last[1], users: group});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
allow_unread() {
|
||||
return (
|
||||
this.channel == '@' ||
|
||||
(!this.channel.startsWith('@') && !this.channel.startsWith('%'))
|
||||
);
|
||||
}
|
||||
|
||||
render_unread_icon() {
|
||||
return this.allow_unread() && this.message?.rowid >= this.channel_unread
|
||||
? html`✉️`
|
||||
: undefined;
|
||||
}
|
||||
|
||||
render() {
|
||||
let content = this.message?.content;
|
||||
if (this.message?.decrypted?.type == 'post') {
|
||||
@ -540,29 +652,90 @@ class TfMessageElement extends LitElement {
|
||||
let class_background = this.class_background();
|
||||
let self = this;
|
||||
if (this.message?.type === 'contact_group') {
|
||||
return this.render_frame(
|
||||
html` ${this.message.messages.map(
|
||||
(x) =>
|
||||
html`<tf-message
|
||||
.message=${x}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
></tf-message>`
|
||||
)}`
|
||||
);
|
||||
if (this.expanded[this.expanded_key()]) {
|
||||
return this.render_frame(html`
|
||||
<div class="w3-padding">
|
||||
${this.message.messages.map(
|
||||
(x) =>
|
||||
html`<tf-message
|
||||
.message=${x}
|
||||
whoami=${this.whoami}
|
||||
.users=${this.users}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
></tf-message>`
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||
style="box-sizing: border-box"
|
||||
@click=${() => self.set_expanded(false)}
|
||||
>
|
||||
Collapse
|
||||
</button>
|
||||
`);
|
||||
} else {
|
||||
return this.render_frame(html`
|
||||
<div class="w3-padding">
|
||||
${this.content_group_by_author().map(
|
||||
(x) => html`
|
||||
<div>
|
||||
<tf-user id=${x.author} .users=${this.users}></tf-user>
|
||||
${x.action}
|
||||
${x.users.map(
|
||||
(y) => html`
|
||||
<tf-user id=${y} .users=${this.users}></tf-user>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
`
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
class="w3-button w3-theme-d1 w3-block w3-bar"
|
||||
style="box-sizing: border-box"
|
||||
@click=${() => self.set_expanded(true)}
|
||||
>
|
||||
Expand
|
||||
</button>
|
||||
`);
|
||||
}
|
||||
} else if (this.message.placeholder) {
|
||||
return this.render_frame(
|
||||
html`<div class="w3-padding">
|
||||
<p>
|
||||
<a target="_top" href=${'#' + encodeURIComponent(this.message.id)}
|
||||
>${this.message.id}</a
|
||||
html`<div>
|
||||
<div class="w3-bar">
|
||||
<a
|
||||
class="w3-bar-item w3-panel w3-round-xlarge w3-theme-d1 w3-margin w3-button"
|
||||
target="_top"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>
|
||||
(placeholder)
|
||||
</p>
|
||||
This message is not currently available.
|
||||
</a>
|
||||
<div class="w3-bar-item w3-right">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
%
|
||||
</button>
|
||||
<div
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||
style="right: 48px"
|
||||
>
|
||||
<a
|
||||
target="_top"
|
||||
class="w3-button w3-bar-item"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>View Message</a
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.copy_id}
|
||||
>
|
||||
Copy ID
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>${this.render_votes()}</div>
|
||||
${(this.message.child_messages || []).map(
|
||||
(x) => html`
|
||||
@ -589,7 +762,7 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
if (content.image !== undefined) {
|
||||
image = html`
|
||||
<div><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||
<div @click=${this.body_click}><img src=${'/' + (typeof content.image?.link == 'string' ? content.image.link : content.image) + '/view'} style="width: 256px; height: auto"></img></div>
|
||||
`;
|
||||
}
|
||||
if (content.description !== undefined) {
|
||||
@ -612,25 +785,60 @@ class TfMessageElement extends LitElement {
|
||||
</div>
|
||||
`);
|
||||
} else if (content.type == 'contact') {
|
||||
return html`
|
||||
<div class="w3-padding">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
is
|
||||
${content.blocking === true
|
||||
? 'blocking'
|
||||
: content.blocking === false
|
||||
? 'no longer blocking'
|
||||
: content.following === true
|
||||
? 'following'
|
||||
: content.following === false
|
||||
? 'no longer following'
|
||||
: '?'}
|
||||
<tf-user
|
||||
id=${this.message.content.contact}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
return this.render_frame(html`
|
||||
<div class="w3-bar">
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${this.message.author} .users=${this.users}></tf-user>
|
||||
is
|
||||
${content.blocking === true
|
||||
? 'blocking'
|
||||
: content.blocking === false
|
||||
? 'no longer blocking'
|
||||
: content.following === true
|
||||
? 'following'
|
||||
: content.following === false
|
||||
? 'no longer following'
|
||||
: '?'}
|
||||
<tf-user
|
||||
id=${this.message.content.contact}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
</div>
|
||||
<div class="w3-bar-item w3-right">
|
||||
<button class="w3-button w3-theme-d1" @click=${this.toggle_menu}>
|
||||
%
|
||||
</button>
|
||||
<div
|
||||
class="w3-dropdown-content w3-bar-block w3-card-4 w3-theme-l1"
|
||||
style="right: 48px"
|
||||
>
|
||||
<a
|
||||
target="_top"
|
||||
class="w3-button w3-bar-item"
|
||||
href=${'#' + encodeURIComponent(this.message?.id)}
|
||||
>View Message</a
|
||||
>
|
||||
<button
|
||||
class="w3-button w3-bar-item w3-border-bottom"
|
||||
@click=${this.copy_id}
|
||||
>
|
||||
Copy ID
|
||||
</button>
|
||||
${this.drafts[this.message?.id] === undefined
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-bar-item"
|
||||
@click=${this.show_reply}
|
||||
>
|
||||
↩️ Reply
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
</div>
|
||||
</div>
|
||||
${this.render_votes()} ${this.render_actions()}
|
||||
</div>
|
||||
`;
|
||||
`);
|
||||
} else if (content.type == 'post') {
|
||||
let self = this;
|
||||
let body;
|
||||
@ -653,11 +861,14 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
let content_warning = html`
|
||||
<div
|
||||
class="w3-panel w3-round-xlarge w3-theme-l4"
|
||||
class="w3-panel w3-round-xlarge w3-theme-l4 w3"
|
||||
style="cursor: pointer"
|
||||
@click=${(x) => this.toggle_expanded(':cw')}
|
||||
>
|
||||
<p>${content.contentWarning}</p>
|
||||
<p class="w3-small">
|
||||
${this.is_expanded(':cw') ? 'Show less' : 'Show more'}
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
let content_html = html`
|
||||
@ -775,7 +986,9 @@ class TfMessageElement extends LitElement {
|
||||
}
|
||||
} else {
|
||||
return this.render_small_frame(
|
||||
html`<div class="w3-container"><b>type</b>: ${content.type}</div>`
|
||||
html`<div class="w3-container">
|
||||
<p><b>type</b>: ${content.type}</p>
|
||||
</div>`
|
||||
);
|
||||
}
|
||||
} else if (typeof this.message.content == 'string') {
|
||||
|
@ -13,6 +13,8 @@ class TfNewsElement extends LitElement {
|
||||
expanded: {type: Object},
|
||||
channel: {type: String},
|
||||
channel_unread: {type: Number},
|
||||
recent_reactions: {type: Array},
|
||||
hash: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,6 +30,7 @@ class TfNewsElement extends LitElement {
|
||||
this.drafts = {};
|
||||
this.expanded = {};
|
||||
this.channel_unread = -1;
|
||||
this.recent_reactions = [];
|
||||
}
|
||||
|
||||
process_messages(messages) {
|
||||
@ -164,7 +167,10 @@ class TfNewsElement extends LitElement {
|
||||
if (message?.content?.type === 'contact') {
|
||||
group.push(message);
|
||||
} else {
|
||||
if (group.length > 0) {
|
||||
if (group.length == 1) {
|
||||
result.push(group[0]);
|
||||
group = [];
|
||||
} else if (group.length > 1) {
|
||||
result.push({
|
||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||
type: 'contact_group',
|
||||
@ -175,7 +181,10 @@ class TfNewsElement extends LitElement {
|
||||
result.push(message);
|
||||
}
|
||||
}
|
||||
if (group.length > 0) {
|
||||
if (group.length == 1) {
|
||||
result.push(group[0]);
|
||||
group = [];
|
||||
} else if (group.length > 1) {
|
||||
result.push({
|
||||
rowid: Math.max(...group.map((x) => x.rowid)),
|
||||
type: 'contact_group',
|
||||
@ -185,15 +194,21 @@ class TfNewsElement extends LitElement {
|
||||
return result;
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return !this.hash?.startsWith('#%') && !this.hash?.startsWith('#@');
|
||||
}
|
||||
|
||||
load_and_render(messages) {
|
||||
let messages_by_id = this.process_messages(messages);
|
||||
let final_messages = this.group_following(
|
||||
this.finalize_messages(messages_by_id)
|
||||
);
|
||||
let unread_rowid = -1;
|
||||
for (let message of final_messages) {
|
||||
if (message.rowid >= this.channel_unread) {
|
||||
unread_rowid = message.rowid;
|
||||
if (this.unread_allowed()) {
|
||||
for (let message of final_messages) {
|
||||
if (message.rowid >= this.channel_unread) {
|
||||
unread_rowid = message.rowid;
|
||||
}
|
||||
}
|
||||
}
|
||||
return html`
|
||||
@ -211,6 +226,7 @@ class TfNewsElement extends LitElement {
|
||||
collapsed="true"
|
||||
channel=${this.channel}
|
||||
channel_unread=${this.channel_unread}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-message>
|
||||
${x.rowid == unread_rowid
|
||||
? html`<div style="display: flex; flex-direction: row">
|
||||
|
@ -1,4 +1,4 @@
|
||||
import {LitElement, html, unsafeHTML} from './lit-all.min.js';
|
||||
import {LitElement, html, until, unsafeHTML} from './lit-all.min.js';
|
||||
import * as tfrpc from '/static/tfrpc.js';
|
||||
import * as tfutils from './tf-utils.js';
|
||||
import {styles} from './tf-styles.js';
|
||||
@ -11,6 +11,7 @@ class TfProfileElement extends LitElement {
|
||||
id: {type: String},
|
||||
users: {type: Object},
|
||||
size: {type: Number},
|
||||
sequence: {type: Number},
|
||||
following: {type: Boolean},
|
||||
blocking: {type: Boolean},
|
||||
};
|
||||
@ -26,6 +27,7 @@ class TfProfileElement extends LitElement {
|
||||
this.id = null;
|
||||
this.users = {};
|
||||
this.size = 0;
|
||||
this.sequence = 0;
|
||||
}
|
||||
|
||||
async load() {
|
||||
@ -62,6 +64,7 @@ class TfProfileElement extends LitElement {
|
||||
}
|
||||
|
||||
modify(change) {
|
||||
let self = this;
|
||||
tfrpc.rpc
|
||||
.appendMessage(
|
||||
this.whoami,
|
||||
@ -73,6 +76,10 @@ class TfProfileElement extends LitElement {
|
||||
change
|
||||
)
|
||||
)
|
||||
.then(function () {
|
||||
self._follow_whoami = undefined;
|
||||
self.load();
|
||||
})
|
||||
.catch(function (error) {
|
||||
alert(error?.message);
|
||||
});
|
||||
@ -134,7 +141,8 @@ class TfProfileElement extends LitElement {
|
||||
let self = this;
|
||||
let input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.onchange = function (event) {
|
||||
input.addEventListener('change', function (event) {
|
||||
input.parentNode.removeChild(input);
|
||||
let file = event.target.files[0];
|
||||
file
|
||||
.arrayBuffer()
|
||||
@ -149,7 +157,8 @@ class TfProfileElement extends LitElement {
|
||||
.catch(function (e) {
|
||||
alert(e.message);
|
||||
});
|
||||
};
|
||||
});
|
||||
document.body.appendChild(input);
|
||||
input.click();
|
||||
}
|
||||
|
||||
@ -157,17 +166,86 @@ class TfProfileElement extends LitElement {
|
||||
navigator.clipboard.writeText(this.id);
|
||||
}
|
||||
|
||||
show_image(link) {
|
||||
let div = document.createElement('div');
|
||||
div.style.left = 0;
|
||||
div.style.top = 0;
|
||||
div.style.width = '100%';
|
||||
div.style.height = '100%';
|
||||
div.style.position = 'fixed';
|
||||
div.style.background = '#000';
|
||||
div.style.zIndex = 100;
|
||||
div.style.display = 'grid';
|
||||
let img = document.createElement('img');
|
||||
img.src = link;
|
||||
img.style.maxWidth = '100%';
|
||||
img.style.maxHeight = '100%';
|
||||
img.style.display = 'block';
|
||||
img.style.margin = 'auto';
|
||||
img.style.objectFit = 'contain';
|
||||
img.style.width = '100%';
|
||||
div.appendChild(img);
|
||||
function image_close(event) {
|
||||
document.body.removeChild(div);
|
||||
window.removeEventListener('keydown', image_close);
|
||||
}
|
||||
div.onclick = image_close;
|
||||
window.addEventListener('keydown', image_close);
|
||||
document.body.appendChild(div);
|
||||
}
|
||||
|
||||
body_click(event) {
|
||||
if (event.srcElement.tagName == 'IMG') {
|
||||
this.show_image(event.srcElement.src);
|
||||
}
|
||||
}
|
||||
|
||||
toggle_account_list(event) {
|
||||
let content = event.srcElement.nextElementSibling;
|
||||
if (content.classList.toggle('w3-hide')) {
|
||||
event.srcElement.innerText = 'Show Followed Accounts';
|
||||
} else {
|
||||
event.srcElement.innerText = 'Hide Followed Accounts';
|
||||
}
|
||||
}
|
||||
|
||||
async load_follows() {
|
||||
let accounts = await tfrpc.rpc.following([this.id], 1);
|
||||
return html`
|
||||
<div class="w3-container">
|
||||
<button
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${this.toggle_account_list}
|
||||
>
|
||||
Show Followed Accounts
|
||||
</button>
|
||||
<div class="w3-hide w3-card">
|
||||
<ul class="w3-ul w3-theme-d4 w3-border-theme">
|
||||
${Object.keys(accounts).map(
|
||||
(x) => html`
|
||||
<li class="w3-border-theme">
|
||||
<tf-user id=${x} .users=${this.users}></tf-user>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
this.load();
|
||||
let self = this;
|
||||
let profile = this.users[this.id] || {};
|
||||
tfrpc.rpc
|
||||
.query(
|
||||
`SELECT SUM(LENGTH(content)) AS size FROM messages WHERE author = ?`,
|
||||
`SELECT SUM(LENGTH(content)) AS size, MAX(sequence) AS sequence FROM messages WHERE author = ?`,
|
||||
[this.id]
|
||||
)
|
||||
.then(function (result) {
|
||||
self.size = result[0].size;
|
||||
self.sequence = result[0].sequence;
|
||||
});
|
||||
let edit;
|
||||
let follow;
|
||||
@ -232,15 +310,19 @@ class TfProfileElement extends LitElement {
|
||||
</div>
|
||||
</div>`
|
||||
: null;
|
||||
let image =
|
||||
typeof profile.image == 'string' ? profile.image : profile.image?.link;
|
||||
let image = profile.image;
|
||||
if (typeof image == 'string' && !image.startsWith('&')) {
|
||||
try {
|
||||
image = JSON.parse(image)?.link;
|
||||
} catch {}
|
||||
}
|
||||
image = this.editing?.image ?? image;
|
||||
let description = this.editing?.description ?? profile.description;
|
||||
return html`<div class="w3-card-4 w3-container w3-theme-d3" style="box-sizing: border-box">
|
||||
<header class="w3-container">
|
||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)})</p>
|
||||
<p><tf-user id=${this.id} .users=${this.users}></tf-user> (${tfutils.human_readable_size(this.size)} in ${this.sequence} messages)</p>
|
||||
</header>
|
||||
<div class="w3-container">
|
||||
<div class="w3-container" @click=${this.body_click}>
|
||||
<div class="w3-margin-bottom" style="display: flex; flex-direction: row">
|
||||
<input type="text" class="w3-input w3-border w3-theme-d1" style="display: flex 1 1" readonly value=${this.id}></input>
|
||||
<button class="w3-button w3-theme-d1 w3-ripple" style="flex: 0 0 auto" @click=${this.copy_id}>Copy</button>
|
||||
@ -266,6 +348,7 @@ class TfProfileElement extends LitElement {
|
||||
Blocked by ${profile.blocked} identities.
|
||||
</div>
|
||||
</div>
|
||||
${until(this.load_follows(), html`<p>Loading accounts followed...</p>`)}
|
||||
<footer class="w3-container">
|
||||
<p>
|
||||
${edit}
|
||||
|
@ -41,23 +41,26 @@ class TfReactionsModalElement extends LitElement {
|
||||
>
|
||||
</header>
|
||||
<ul class="w3-theme-dark w3-container w3-ul">
|
||||
${this.votes.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">
|
||||
<span class="w3-bar-item"
|
||||
>${x?.content?.vote?.expression}</span
|
||||
>
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
id=${x.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
<span class="w3-bar-item w3-right"
|
||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
${this.votes
|
||||
.sort((x, y) => y.timestamp - x.timestamp)
|
||||
.map(
|
||||
(x) => html`
|
||||
<li style="display: flex; flex-direction: row; gap: 4px">
|
||||
<span style="flex-basis: 3em"
|
||||
>${x?.content?.vote?.expression}</span
|
||||
>
|
||||
<tf-user
|
||||
style="flex: 1 1"
|
||||
id=${x.author}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
<span
|
||||
style="flex-shrink: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis"
|
||||
>${new Date(x?.timestamp).toLocaleString()}</span
|
||||
>
|
||||
</li>
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<footer class="w3-container w3-padding">
|
||||
<button class="w3-button" @click=${this.clear}>Close</button>
|
||||
|
@ -48,7 +48,7 @@ const tf = css`
|
||||
|
||||
// prettier-ignore
|
||||
const w3 = css`
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.01 March 14 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -88,7 +88,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-table th:first-child,.w3-table td:first-child,.w3-table-all th:first-child,.w3-table-all td:first-child{padding-left:16px}
|
||||
.w3-btn,.w3-button{border:none;display:inline-block;padding:8px 16px;vertical-align:middle;overflow:hidden;text-decoration:none;color:inherit;background-color:inherit;text-align:center;cursor:pointer;white-space:nowrap}
|
||||
.w3-btn:hover{box-shadow:0 8px 16px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19)}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-btn,.w3-button{-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}
|
||||
.w3-disabled,.w3-btn:disabled,.w3-button:disabled{cursor:not-allowed;opacity:0.3}.w3-disabled *,:disabled *{pointer-events:none}
|
||||
.w3-btn.w3-disabled:hover,.w3-btn:disabled:hover{box-shadow:none}
|
||||
.w3-badge,.w3-tag{background-color:#000;color:#fff;display:inline-block;padding-left:8px;padding-right:8px;text-align:center}.w3-badge{border-radius:50%}
|
||||
@ -136,7 +136,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-hide{display:none!important}.w3-show-block,.w3-show{display:block!important}.w3-show-inline-block{display:inline-block!important}
|
||||
@media (max-width:1205px){.w3-auto{max-width:95%}}
|
||||
@media (max-width:600px){.w3-modal-content{margin:0 10px;width:auto!important}.w3-modal{padding-top:30px}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-dropdown-hover.w3-mobile .w3-dropdown-content,.w3-dropdown-click.w3-mobile .w3-dropdown-content{position:relative}
|
||||
.w3-hide-small{display:none!important}.w3-mobile{display:block;width:100%!important}.w3-bar-item.w3-mobile,.w3-dropdown-hover.w3-mobile,.w3-dropdown-click.w3-mobile{text-align:center}
|
||||
.w3-dropdown-hover.w3-mobile,.w3-dropdown-hover.w3-mobile .w3-btn,.w3-dropdown-hover.w3-mobile .w3-button,.w3-dropdown-click.w3-mobile,.w3-dropdown-click.w3-mobile .w3-btn,.w3-dropdown-click.w3-mobile .w3-button{width:100%}}
|
||||
@media (max-width:768px){.w3-modal-content{width:500px}.w3-modal{padding-top:50px}}
|
||||
@ -158,6 +158,10 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -199,9 +203,9 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-amber,.w3-hover-amber:hover,.w3-warning{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
.w3-blue,.w3-hover-blue:hover{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-blue,.w3-hover-blue:hover,.w3-info,.w3-primary{color:#fff!important;background-color:#2196F3!important}
|
||||
.w3-light-blue,.w3-hover-light-blue:hover{color:#000!important;background-color:#87CEEB!important}
|
||||
.w3-brown,.w3-hover-brown:hover{color:#fff!important;background-color:#795548!important}
|
||||
.w3-cyan,.w3-hover-cyan:hover{color:#000!important;background-color:#00bcd4!important}
|
||||
@ -216,15 +220,24 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-pink,.w3-hover-pink:hover{color:#fff!important;background-color:#e91e63!important}
|
||||
.w3-purple,.w3-hover-purple:hover{color:#fff!important;background-color:#9c27b0!important}
|
||||
.w3-deep-purple,.w3-hover-deep-purple:hover{color:#fff!important;background-color:#673ab7!important}
|
||||
.w3-red,.w3-hover-red:hover{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-red,.w3-hover-red:hover,.w3-danger{color:#fff!important;background-color:#f44336!important}
|
||||
.w3-sand,.w3-hover-sand:hover{color:#000!important;background-color:#fdf5e6!important}
|
||||
.w3-teal,.w3-hover-teal:hover{color:#fff!important;background-color:#009688!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-yellow,.w3-hover-yellow:hover,.w3-note{color:#000!important;background-color:#ffeb3b!important}
|
||||
.w3-white,.w3-hover-white:hover{color:#000!important;background-color:#fff!important}
|
||||
.w3-black,.w3-hover-black:hover{color:#fff!important;background-color:#000!important}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover,.w3-secondary{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover,.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
|
@ -103,6 +103,23 @@ class TfTabConnectionsElement extends LitElement {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
render_progress(name, value, max) {
|
||||
if (max && value != max) {
|
||||
return html`
|
||||
<div class="w3-theme-d1 w3-small">
|
||||
<div
|
||||
class="w3-container w3-theme-l1"
|
||||
style="width: ${Math.floor(
|
||||
(100.0 * value) / max
|
||||
)}%; text-wrap: nowrap"
|
||||
>
|
||||
${name} ${value} / ${max} (${Math.round((100.0 * value) / max)}%)
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
render_broadcast(connection) {
|
||||
let self = this;
|
||||
return html`
|
||||
@ -154,6 +171,16 @@ class TfTabConnectionsElement extends LitElement {
|
||||
: undefined}
|
||||
${connection.flags.one_shot ? '🔃' : undefined}
|
||||
<tf-user id=${connection.id} .users=${this.users}></tf-user>
|
||||
${this.render_progress(
|
||||
'recv',
|
||||
connection.progress.in.total - connection.progress.in.current,
|
||||
connection.progress.in.total
|
||||
)}
|
||||
${this.render_progress(
|
||||
'send',
|
||||
connection.progress.out.total - connection.progress.out.current,
|
||||
connection.progress.out.total
|
||||
)}
|
||||
${connection.tunnel !== undefined
|
||||
? '🚇'
|
||||
: html`(${connection.host}:${connection.port})`}
|
||||
@ -206,6 +233,21 @@ class TfTabConnectionsElement extends LitElement {
|
||||
});
|
||||
}
|
||||
|
||||
toggle_accordian(id) {
|
||||
let element = this.renderRoot.getElementById(id);
|
||||
element.classList.toggle('w3-hide');
|
||||
}
|
||||
|
||||
valid_connections() {
|
||||
return this.connections.filter((x) => x.tunnel === undefined);
|
||||
}
|
||||
|
||||
valid_broadcasts() {
|
||||
return this.broadcasts
|
||||
.filter((x) => x.address)
|
||||
.filter((x) => this.connections.map((c) => c.id).indexOf(x.pubkey) == -1);
|
||||
}
|
||||
|
||||
render() {
|
||||
let self = this;
|
||||
return html`
|
||||
@ -220,27 +262,33 @@ class TfTabConnectionsElement extends LitElement {
|
||||
>
|
||||
Connect
|
||||
</button>
|
||||
<h2>Broadcasts</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.broadcasts
|
||||
.filter((x) => x.address)
|
||||
.filter(
|
||||
(x) => self.connections.map((c) => c.id).indexOf(x.pubkey) == -1
|
||||
)
|
||||
.map((x) => self.render_broadcast(x))}
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('connections')}
|
||||
>
|
||||
Connections (${this.valid_connections().length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border" id="connections">
|
||||
${this.valid_connections().map(
|
||||
(x) => html` <li class="w3-bar">${this.render_connection(x)}</li> `
|
||||
)}
|
||||
</ul>
|
||||
<h2>Connections</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
${this.connections
|
||||
.filter((x) => x.tunnel === undefined)
|
||||
.map(
|
||||
(x) => html`
|
||||
<li class="w3-bar">${this.render_connection(x)}</li>
|
||||
`
|
||||
)}
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('broadcasts')}
|
||||
>
|
||||
Discovery (${this.valid_broadcasts().length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border w3-hide" id="broadcasts">
|
||||
${this.valid_broadcasts().map((x) => self.render_broadcast(x))}
|
||||
</ul>
|
||||
<h2>Stored Connections</h2>
|
||||
<ul class="w3-ul w3-border">
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('stored_connections')}
|
||||
>
|
||||
Stored Connections (${this.stored_connections.length})
|
||||
</h2>
|
||||
<ul class="w3-ul w3-border w3-hide" id="stored_connections">
|
||||
${this.stored_connections.map(
|
||||
(x) => html`
|
||||
<li>
|
||||
@ -260,6 +308,12 @@ class TfTabConnectionsElement extends LitElement {
|
||||
<div class="w3-bar-item">
|
||||
<tf-user id=${x.pubkey} .users=${self.users}></tf-user>
|
||||
<div><small>${x.address}:${x.port}</small></div>
|
||||
<div>
|
||||
<small
|
||||
>Last connection:
|
||||
${new Date(x.last_success * 1000)}</small
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
${this.render_message(x)}
|
||||
@ -267,8 +321,13 @@ class TfTabConnectionsElement extends LitElement {
|
||||
`
|
||||
)}
|
||||
</ul>
|
||||
<h2>Local Accounts</h2>
|
||||
<div class="w3-container">
|
||||
<h2
|
||||
class="w3-button w3-block w3-theme-d1"
|
||||
@click=${() => self.toggle_accordian('local_accounts')}
|
||||
>
|
||||
Local Accounts (${this.identities.length})
|
||||
</h2>
|
||||
<div class="w3-container w3-hide" id="local_accounts">
|
||||
${this.identities.map(
|
||||
(x) =>
|
||||
html`<div
|
||||
|
@ -17,6 +17,8 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
loading: {type: Number},
|
||||
time_range: {type: Array},
|
||||
time_loading: {type: Array},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
};
|
||||
}
|
||||
|
||||
@ -36,6 +38,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
this.start_time = new Date().valueOf();
|
||||
this.time_range = [0, 0];
|
||||
this.time_loading = undefined;
|
||||
this.recent_reactions = [];
|
||||
this.loading = 0;
|
||||
}
|
||||
|
||||
@ -45,6 +48,63 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
: this.hash.substring(1);
|
||||
}
|
||||
|
||||
async _fetch_related_messages(messages) {
|
||||
let refs = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
news AS (
|
||||
SELECT value AS id FROM json_each(?)
|
||||
)
|
||||
SELECT refs_out.ref AS ref FROM messages_refs refs_out JOIN news ON refs_out.message = news.id
|
||||
UNION
|
||||
SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id
|
||||
`,
|
||||
[JSON.stringify(messages.map((x) => x.id))]
|
||||
);
|
||||
let related_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?2) refs ON messages.id = refs.value
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
`,
|
||||
[JSON.stringify(this.following), JSON.stringify(refs.map((x) => x.ref))]
|
||||
);
|
||||
let combined = [].concat(messages, related_messages);
|
||||
let refs2 = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
news AS (
|
||||
SELECT value AS id FROM json_each(?)
|
||||
)
|
||||
SELECT refs_out.ref AS ref FROM messages_refs refs_out JOIN news ON refs_out.message = news.id
|
||||
UNION
|
||||
SELECT refs_in.message AS ref FROM messages_refs refs_in JOIN news ON refs_in.ref = news.id
|
||||
`,
|
||||
[JSON.stringify(combined.map((x) => x.id))]
|
||||
);
|
||||
let t0 = new Date();
|
||||
let result = [].concat(
|
||||
combined,
|
||||
await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM json_each(?2) refs
|
||||
JOIN messages ON messages.id = refs.value
|
||||
JOIN json_each(?1) following ON messages.author = following.value
|
||||
WHERE messages.content ->> 'type' != 'post'
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.following),
|
||||
JSON.stringify(refs2.map((x) => x.ref)),
|
||||
]
|
||||
)
|
||||
);
|
||||
let t1 = new Date();
|
||||
console.log((t1 - t0) / 1000);
|
||||
return result;
|
||||
}
|
||||
|
||||
async fetch_messages(start_time, end_time) {
|
||||
this.time_loading = [start_time, end_time];
|
||||
let result;
|
||||
@ -106,7 +166,8 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
[this.hash.substring(1)]
|
||||
);
|
||||
} else if (this.hash.startsWith('##')) {
|
||||
result = await tfrpc.rpc.query(
|
||||
let t0 = new Date();
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
all_news AS (
|
||||
@ -116,75 +177,85 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
WHERE messages.content ->> 'channel' = ?4
|
||||
UNION
|
||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages_fts(?5)
|
||||
JOIN messages ON messages.rowid = messages_fts.rowid
|
||||
FROM messages_refs
|
||||
JOIN messages ON messages.id = messages_refs.message
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
JOIN json_tree(messages.content, '$.mentions') AS mention ON mention.value = '#' || ?4),
|
||||
news AS (SELECT * FROM all_news
|
||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||
ORDER BY all_news.timestamp DESC LIMIT 20)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, news.* FROM news
|
||||
WHERE messages_refs.ref = '#' || ?4
|
||||
)
|
||||
SELECT TRUE AS is_primary, all_news.* FROM all_news
|
||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||
ORDER BY all_news.timestamp DESC LIMIT 20
|
||||
`,
|
||||
[
|
||||
JSON.stringify(this.following),
|
||||
start_time,
|
||||
end_time,
|
||||
this.hash.substring(2),
|
||||
'"#' + this.hash.substring(2).replace('"', '""') + '"',
|
||||
]
|
||||
);
|
||||
let t1 = new Date();
|
||||
result = await this._fetch_related_messages(initial_messages);
|
||||
let t2 = new Date();
|
||||
console.log(
|
||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
||||
);
|
||||
} else if (this.hash == '#🔐') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
SELECT TRUE AS is_primary, messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
|
||||
FROM messages
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
JOIN json_each(?1) AS private_messages ON messages.id = private_messages.value
|
||||
WHERE
|
||||
(?2 IS NULL OR (messages.timestamp >= ?2)) AND messages.timestamp < ?3 AND
|
||||
json(messages.content) LIKE '"%'
|
||||
ORDER BY messages.sequence DESC LIMIT 20
|
||||
ORDER BY messages.rowid DESC LIMIT 20
|
||||
`,
|
||||
[JSON.stringify(this.private_messages), start_time, end_time]
|
||||
);
|
||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
} else if (this.hash == '#👍') {
|
||||
result = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH votes AS (SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?1) AS following ON messages.author = following.value
|
||||
WHERE
|
||||
messages.content ->> 'type' = 'vote' AND
|
||||
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
|
||||
ORDER BY timestamp DESC limit 20)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM votes
|
||||
JOIN messages ON messages.id = votes.content ->> '$.vote.link'
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, * FROM votes
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time]
|
||||
);
|
||||
result = (await this.decrypt(result)).filter((x) => x.decrypted);
|
||||
} else {
|
||||
result = await tfrpc.rpc.query(
|
||||
let t0 = new Date();
|
||||
let initial_messages = await tfrpc.rpc.query(
|
||||
`
|
||||
WITH
|
||||
all_news AS (
|
||||
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM messages
|
||||
JOIN json_each(?) AS following ON messages.author = following.value
|
||||
WHERE timestamp >= 0 AND timestamp < ?3),
|
||||
),
|
||||
news AS (
|
||||
SELECT * FROM all_news
|
||||
WHERE (?2 IS NULL OR all_news.timestamp >= ?2) AND all_news.timestamp < ?3
|
||||
WHERE all_news.timestamp < ?3 AND (?2 IS NULL OR all_news.timestamp >= ?2)
|
||||
ORDER BY timestamp DESC LIMIT 20
|
||||
)
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.ref
|
||||
JOIN messages ON messages_refs.message = messages.id
|
||||
UNION
|
||||
SELECT FALSE AS is_primary, messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
|
||||
FROM news
|
||||
JOIN messages_refs ON news.id = messages_refs.message
|
||||
JOIN messages ON messages_refs.ref = messages.id
|
||||
UNION
|
||||
SELECT TRUE AS is_primary, news.* FROM news
|
||||
`,
|
||||
[JSON.stringify(this.following), start_time, end_time]
|
||||
);
|
||||
let t1 = new Date();
|
||||
result = await this._fetch_related_messages(initial_messages);
|
||||
let t2 = new Date();
|
||||
console.log(
|
||||
`load of ${result.length} rows took ${(t2 - t0) / 1000} (${(t1 - t0) / 1000} to find ${initial_messages.length} initial messages, ${(t2 - t1) / 1000} to find ${result.length} total messages) following=${this.following.length} st=${start_time} et=${end_time}`
|
||||
);
|
||||
}
|
||||
this.time_loading = undefined;
|
||||
return result;
|
||||
@ -204,13 +275,24 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
];
|
||||
}
|
||||
|
||||
unread_allowed() {
|
||||
return (
|
||||
this.hash == '#@' ||
|
||||
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
|
||||
);
|
||||
}
|
||||
|
||||
async load_more() {
|
||||
this.loading++;
|
||||
this.loading_canceled = false;
|
||||
try {
|
||||
let more = [];
|
||||
let last_start_time = this.time_range[0];
|
||||
more = await this.fetch_messages(null, last_start_time);
|
||||
try {
|
||||
more = await this.fetch_messages(null, last_start_time);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
this.update_time_range_from_messages(
|
||||
more.filter((x) => x.timestamp < last_start_time)
|
||||
);
|
||||
@ -298,7 +380,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
let now = new Date().valueOf();
|
||||
let start_time = now - 24 * 60 * 60 * 1000;
|
||||
this.start_time = start_time;
|
||||
this.time_range = [this.start_time, now + 24 * 60 * 60 * 1000];
|
||||
this.time_range = [now + 24 * 60 * 60 * 1000, now + 24 * 60 * 60 * 1000];
|
||||
messages = await this.fetch_messages(null, this.time_range[1]);
|
||||
this.update_time_range_from_messages(
|
||||
messages.filter((x) => x.timestamp < this.time_range[1])
|
||||
@ -310,7 +392,7 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
this.messages = this.merge_messages(this.messages, messages);
|
||||
this.time_loading = undefined;
|
||||
console.log(
|
||||
`loading messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
||||
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
|
||||
);
|
||||
}
|
||||
|
||||
@ -349,9 +431,16 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
if (!this.hash.startsWith('#%')) {
|
||||
more = html`
|
||||
<p>
|
||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
||||
Mark All Read
|
||||
</button>
|
||||
${this.unread_allowed()
|
||||
? html`
|
||||
<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
<button
|
||||
?disabled=${this.loading}
|
||||
class="w3-button w3-theme-d1"
|
||||
@ -383,9 +472,14 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
`;
|
||||
}
|
||||
return cache(html`
|
||||
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
|
||||
Mark All Read
|
||||
</button>
|
||||
${this.unread_allowed()
|
||||
? html`<button
|
||||
class="w3-button w3-theme-d1"
|
||||
@click=${this.mark_all_read}
|
||||
>
|
||||
Mark All Read
|
||||
</button>`
|
||||
: undefined}
|
||||
<tf-news
|
||||
id="news"
|
||||
whoami=${this.whoami}
|
||||
@ -394,8 +488,10 @@ class TfTabNewsFeedElement extends LitElement {
|
||||
.following=${this.following}
|
||||
.drafts=${this.drafts}
|
||||
.expanded=${this.expanded}
|
||||
hash=${this.hash}
|
||||
channel=${this.channel()}
|
||||
channel_unread=${this.channels_unread?.[this.channel()]}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-news>
|
||||
${more}
|
||||
`);
|
||||
|
@ -23,6 +23,9 @@ class TfTabNewsElement extends LitElement {
|
||||
channels_unread: {type: Object},
|
||||
channels_latest: {type: Object},
|
||||
connections: {type: Array},
|
||||
private_messages: {type: Array},
|
||||
recent_reactions: {type: Array},
|
||||
peer_exchange: {type: Boolean},
|
||||
};
|
||||
}
|
||||
|
||||
@ -42,9 +45,11 @@ class TfTabNewsElement extends LitElement {
|
||||
this.channels_latest = {};
|
||||
this.channels = [];
|
||||
this.connections = [];
|
||||
this.recent_reactions = [];
|
||||
tfrpc.rpc.localStorageGet('drafts').then(function (d) {
|
||||
self.drafts = JSON.parse(d || '{}');
|
||||
});
|
||||
this.check_peer_exchange();
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@ -57,6 +62,14 @@ class TfTabNewsElement extends LitElement {
|
||||
document.body.removeEventListener('keypress', this.on_keypress.bind(this));
|
||||
}
|
||||
|
||||
async check_peer_exchange() {
|
||||
if (await tfrpc.rpc.isAdministrator()) {
|
||||
this.peer_exchange = await tfrpc.rpc.globalSettingsGet('peer_exchange');
|
||||
} else {
|
||||
this.peer_exchange = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
load_latest() {
|
||||
let news = this.shadowRoot?.getElementById('news');
|
||||
if (news) {
|
||||
@ -94,7 +107,13 @@ class TfTabNewsElement extends LitElement {
|
||||
}
|
||||
|
||||
unread_status(channel) {
|
||||
if (
|
||||
if (channel === undefined) {
|
||||
if (
|
||||
Object.keys(this.channels_unread).some((x) => this.unread_status(x))
|
||||
) {
|
||||
return '✉️ ';
|
||||
}
|
||||
} else if (
|
||||
this.channels_latest[channel] &&
|
||||
this.channels_latest[channel] > 0 &&
|
||||
(this.channels_unread[channel] === undefined ||
|
||||
@ -155,6 +174,15 @@ class TfTabNewsElement extends LitElement {
|
||||
.map((x) => x[0]);
|
||||
}
|
||||
|
||||
refresh() {
|
||||
tfrpc.rpc.sync();
|
||||
}
|
||||
|
||||
async enable_peer_exchange() {
|
||||
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
|
||||
await this.check_peer_exchange();
|
||||
}
|
||||
|
||||
render_sidebar() {
|
||||
return html`
|
||||
<div
|
||||
@ -193,6 +221,12 @@ class TfTabNewsElement extends LitElement {
|
||||
style=${this.hash == '#@' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('@')}@mentions</a
|
||||
>
|
||||
<a
|
||||
href="#👍"
|
||||
class="w3-bar-item w3-button"
|
||||
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
|
||||
>${this.unread_status('👍')}👍votes</a
|
||||
>
|
||||
<a
|
||||
href="#🔐"
|
||||
class="w3-bar-item w3-button"
|
||||
@ -222,15 +256,44 @@ class TfTabNewsElement extends LitElement {
|
||||
`
|
||||
)}
|
||||
|
||||
<h4 class="w3-bar-item w3-theme-d2">Connections</h4>
|
||||
<a class="w3-bar-item w3-theme-d2 w3-button" href="#connections">
|
||||
<h4 style="margin: 0">Connections</h4>
|
||||
</a>
|
||||
${this.connections?.filter((x) => x.id)?.length == 0
|
||||
? html`
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.connections?.some((x) => x.flags.one_shot)
|
||||
? ' w3-spin'
|
||||
: '')}
|
||||
@click=${this.refresh}
|
||||
>
|
||||
↻ Sync now
|
||||
</button>
|
||||
<button
|
||||
class=${'w3-bar-item w3-button' +
|
||||
(this.peer_exchange !== false ? ' w3-hide' : '')}
|
||||
@click=${this.enable_peer_exchange}
|
||||
>
|
||||
Enable peer exchange
|
||||
</button>
|
||||
`
|
||||
: undefined}
|
||||
${this.connections
|
||||
.filter((x) => x.id && !x.destroy_reason)
|
||||
.filter((x) => x.id)
|
||||
.map(
|
||||
(x) => html`
|
||||
<tf-user
|
||||
class="w3-bar-item"
|
||||
style="max-width: 100%"
|
||||
style=${x.destroy_reason
|
||||
? 'border-left: 4px solid red; border-right: 4px solid red'
|
||||
: x.connected
|
||||
? x.flags?.one_shot
|
||||
? 'border-left: 4px solid blue; border-right: 4px solid blue'
|
||||
: 'border-left: 4px solid green; border-right: 4px solid green'
|
||||
: ''}
|
||||
id=${x.id}
|
||||
fallback_name=${x.host}
|
||||
.users=${this.users}
|
||||
></tf-user>
|
||||
`
|
||||
@ -284,7 +347,7 @@ class TfTabNewsElement extends LitElement {
|
||||
return cache(html`
|
||||
${this.render_sidebar()}
|
||||
<div
|
||||
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto"
|
||||
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout"
|
||||
id="main"
|
||||
class="w3-main"
|
||||
>
|
||||
@ -309,7 +372,7 @@ class TfTabNewsElement extends LitElement {
|
||||
class="w3-button w3-hide-large"
|
||||
@click=${this.show_sidebar}
|
||||
>
|
||||
☰
|
||||
${this.unread_status()}☰
|
||||
</div>
|
||||
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
|
||||
${edit_profile}
|
||||
@ -337,6 +400,8 @@ class TfTabNewsElement extends LitElement {
|
||||
@tf-expand=${this.on_expand}
|
||||
.channels_unread=${this.channels_unread}
|
||||
.channels_latest=${this.channels_latest}
|
||||
.private_messages=${this.private_messages}
|
||||
.recent_reactions=${this.recent_reactions}
|
||||
></tf-tab-news-feed>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -6,6 +6,7 @@ class TfUserElement extends LitElement {
|
||||
static get properties() {
|
||||
return {
|
||||
id: {type: String},
|
||||
fallback_name: {type: String},
|
||||
users: {type: Object},
|
||||
};
|
||||
}
|
||||
@ -15,13 +16,16 @@ class TfUserElement extends LitElement {
|
||||
constructor() {
|
||||
super();
|
||||
this.id = null;
|
||||
this.fallback_name = null;
|
||||
this.users = {};
|
||||
}
|
||||
|
||||
render() {
|
||||
let user = this.users[this.id];
|
||||
let shape =
|
||||
!user?.follow_depth || user.follow_depth >= 2 ? 'w3-circle' : 'w3-round';
|
||||
user?.follow_depth === undefined || user.follow_depth >= 2
|
||||
? 'w3-circle'
|
||||
: 'w3-round';
|
||||
let image = html`<span
|
||||
class=${'w3-theme-l4 ' + shape}
|
||||
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
|
||||
@ -29,13 +33,16 @@ class TfUserElement extends LitElement {
|
||||
>`;
|
||||
let name = this.users?.[this.id]?.name;
|
||||
name = html`<a target="_top" href=${'#' + this.id}
|
||||
>${name !== undefined ? name : this.id}</a
|
||||
>${name ?? this.fallback_name ?? this.id}</a
|
||||
>`;
|
||||
|
||||
if (user) {
|
||||
let image_link = user.image;
|
||||
image_link =
|
||||
typeof image_link == 'string' ? image_link : image_link?.link;
|
||||
if (typeof image_link == 'string' && !image_link.startsWith('&')) {
|
||||
try {
|
||||
image_link = JSON.parse(image_link)?.link;
|
||||
} catch {}
|
||||
}
|
||||
if (image_link !== undefined) {
|
||||
image = html`<img
|
||||
class=${'w3-theme-l4 ' + shape}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "💾",
|
||||
"previous": "&mvGTlWKFR5QM/3nb4fJ2WQq0n/gNKvBmhGDkAvb8ki8=.sha256"
|
||||
"previous": "&tzZFIe7Y54O4sx1QtAPdemkXh+p5qHXSG/dlS7NP6OQ=.sha256"
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ async function query(sql, args) {
|
||||
|
||||
async function get_biggest() {
|
||||
return query(`
|
||||
select author, sum(length(content)) as size from messages group by author order by size desc limit 10;
|
||||
select author, size from messages_stats group by author order by size desc limit 10;
|
||||
`);
|
||||
}
|
||||
|
||||
@ -62,15 +62,14 @@ function nice_size(bytes) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
await app.setDocument(
|
||||
'<p style="color: #fff">Finding the top 10 largest feeds...</p>'
|
||||
);
|
||||
let most_follows = await get_most_follows();
|
||||
await app.setDocument('<p style="color: #fff">Analyzing feeds...</p>');
|
||||
let most_follows = get_most_follows();
|
||||
let total = await get_total();
|
||||
let identities = await ssb.getAllIdentities();
|
||||
let following1 = await ssb.following(identities, 1);
|
||||
let following2 = await ssb.following(identities, 2);
|
||||
let biggest = await get_biggest();
|
||||
most_follows = await most_follows;
|
||||
let names = await get_names(
|
||||
[].concat(
|
||||
biggest.map((x) => x.author),
|
||||
@ -94,7 +93,7 @@ async function main() {
|
||||
}
|
||||
let html = `<body style="color: #000; background-color: #ddd">\n
|
||||
<h1>Storage Summary</h1>
|
||||
<h2>Top 10 Accounts by Size</h2>
|
||||
<h2>Top Accounts by Size</h2>
|
||||
<ol>`;
|
||||
for (let item of biggest) {
|
||||
html += `<li>
|
||||
@ -105,7 +104,7 @@ async function main() {
|
||||
}
|
||||
html += `
|
||||
</ol>
|
||||
<h2>Top 10 Accounts by Follows</h2>
|
||||
<h2>Top Accounts by Follows</h2>
|
||||
<ol>`;
|
||||
for (let item of most_follows) {
|
||||
html += `<li>
|
||||
|
5
apps/web.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "🕸",
|
||||
"previous": "&n7hu5b8/TsfiG6FDlCRG5nPCrIdCr96+xpIJ/aQT/uM=.sha256"
|
||||
}
|
100
apps/web/app.js
Normal file
@ -0,0 +1,100 @@
|
||||
let g_hash;
|
||||
|
||||
async function query(sql, params) {
|
||||
let results = [];
|
||||
await ssb.sqlAsync(sql, params, function (row) {
|
||||
results.push(row);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
async function resolve(id) {
|
||||
try {
|
||||
let blob = await ssb.blobGet(id);
|
||||
if (blob) {
|
||||
let json;
|
||||
try {
|
||||
json = JSON.parse(utf8Decode(blob));
|
||||
} catch {
|
||||
return {id: utf8Decode(blob)};
|
||||
}
|
||||
if (json?.links) {
|
||||
for (let [key, value] of Object.entries(json.links)) {
|
||||
json.links[key] = await resolve(value);
|
||||
}
|
||||
return json;
|
||||
} else {
|
||||
return 'huh?' + json;
|
||||
}
|
||||
} else {
|
||||
return `missing<${id}>`;
|
||||
}
|
||||
} catch (e) {
|
||||
return id + ': ' + e.message;
|
||||
}
|
||||
}
|
||||
|
||||
async function get_names(identities) {
|
||||
return Object.fromEntries(
|
||||
(
|
||||
await query(
|
||||
`
|
||||
SELECT author, name FROM (
|
||||
SELECT
|
||||
messages.author,
|
||||
RANK() OVER (PARTITION BY messages.author ORDER BY messages.sequence DESC) AS author_rank,
|
||||
messages.content ->> 'name' AS name
|
||||
FROM messages
|
||||
JOIN json_each(?) AS identities ON identities.value = messages.author
|
||||
WHERE
|
||||
json_extract(messages.content, '$.type') = 'about' AND
|
||||
content ->> 'about' = messages.author AND name IS NOT NULL)
|
||||
WHERE author_rank = 1
|
||||
`,
|
||||
[JSON.stringify(identities)]
|
||||
)
|
||||
).map((x) => [x.author, x.name])
|
||||
);
|
||||
}
|
||||
|
||||
async function render(hash) {
|
||||
g_hash = hash;
|
||||
if (!hash) {
|
||||
let sites = await query(
|
||||
`
|
||||
SELECT site.author, site.id
|
||||
FROM messages site
|
||||
WHERE site.content ->> 'type' = 'web-init'
|
||||
`,
|
||||
[]
|
||||
);
|
||||
let names = await get_names(sites.map((x) => x.author));
|
||||
if (hash === g_hash) {
|
||||
await app.setDocument(
|
||||
`<ul style="background-color: #ddd">${sites.map((x) => `<li><a target="_top" href="#${encodeURIComponent(x.id)}">${names[x.author] ?? x.author} - ${x.id}</a></li>`).join('\n')}</ul>`
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let site_id =
|
||||
hash.charAt(0) == '#'
|
||||
? decodeURIComponent(hash.substring(1))
|
||||
: decodeURIComponent(hash);
|
||||
await app.setDocument(`<html style="margin: 0; padding: 0; width: 100vw; height: 100vh; margin: 0; padding: 0">
|
||||
<body style="display: flex; flex-direction: column; width: 100vw; height: 100vh">
|
||||
<iframe src="${encodeURIComponent(site_id)}/index.html" style="flex: 1 1; border: 0; background-color: #fff"></iframe>
|
||||
</body>
|
||||
</html>`);
|
||||
}
|
||||
}
|
||||
|
||||
core.register('message', async function message_handler(message) {
|
||||
if (message.event == 'hashChange') {
|
||||
await render(message.hash);
|
||||
}
|
||||
});
|
||||
|
||||
async function main() {
|
||||
render(null);
|
||||
}
|
||||
|
||||
main();
|
63
apps/web/handler.js
Normal file
@ -0,0 +1,63 @@
|
||||
async function query(sql, params) {
|
||||
let results = [];
|
||||
await ssb.sqlAsync(sql, params, function (row) {
|
||||
results.push(row);
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
function guess_content_type(name) {
|
||||
if (name.endsWith('.html')) {
|
||||
return 'text/html; charset=UTF-8';
|
||||
} else if (name.endsWith('.js') || name.endsWith('.mjs')) {
|
||||
return 'text/javascript; charset=UTF-8';
|
||||
} else if (name.endsWith('.css')) {
|
||||
return 'text/stylesheet; charset=UTF-8';
|
||||
} else {
|
||||
return 'application/binary';
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
let path = request.path.replaceAll(/(%[0-9a-fA-F]{2})/g, (x) =>
|
||||
String.fromCharCode(parseInt(x.substring(1), 16))
|
||||
);
|
||||
let match = path.match(/^(%.{44}\.sha256)(?:\/)?(.*)$/);
|
||||
|
||||
let content_type = guess_content_type(request.path);
|
||||
let root = await query(
|
||||
`
|
||||
SELECT root.content ->> 'root' AS root
|
||||
FROM messages site
|
||||
JOIN messages root
|
||||
ON site.id = ? AND root.author = site.author AND root.content ->> 'site' = site.id
|
||||
ORDER BY root.sequence DESC LIMIT 1
|
||||
`,
|
||||
[match[1]]
|
||||
);
|
||||
let root_id = root[0]['root'];
|
||||
let last_id = root_id;
|
||||
let blob = await ssb.blobGet(root_id);
|
||||
try {
|
||||
for (let part of match[2]?.split('/')) {
|
||||
let dir = JSON.parse(utf8Decode(blob));
|
||||
last_id = dir?.links[part];
|
||||
blob = await ssb.blobGet(dir?.links[part]);
|
||||
content_type = guess_content_type(part);
|
||||
}
|
||||
} catch {}
|
||||
|
||||
respond({
|
||||
status_code: 200,
|
||||
data: blob ? utf8Decode(blob) : `${last_id} not found`,
|
||||
content_type: content_type,
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(function (e) {
|
||||
respond({
|
||||
status_code: 200,
|
||||
data: `${e.message}\n${e.stack}`,
|
||||
content_type: 'text/plain',
|
||||
});
|
||||
});
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"type": "tildefriends-app",
|
||||
"emoji": "👋",
|
||||
"previous": "&7gFmLW5zSMhmxWWY1+jeRcHdullgujSqGJg94lVgr1k=.sha256"
|
||||
"previous": "&fY3YUKPuH/wqOgKPVNJu1vWEHCXf5fToL2qiVXMRmxc=.sha256"
|
||||
}
|
||||
|
1521
apps/welcome/hermietildefriends.svg
Normal file
After Width: | Height: | Size: 86 KiB |
@ -10,17 +10,6 @@
|
||||
<link rel="stylesheet" href="brands.min.css" />
|
||||
|
||||
<style>
|
||||
body,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5 {
|
||||
font-family: 'Poppins', sans-serif;
|
||||
}
|
||||
body {
|
||||
font-size: 16px;
|
||||
}
|
||||
img {
|
||||
margin-bottom: -8px;
|
||||
}
|
||||
@ -39,57 +28,37 @@
|
||||
<b>😎 Tilde Friends</b>
|
||||
</h1>
|
||||
<h1 class="w3-xxlarge w3-text-green">
|
||||
<b>Make apps and friends from the comfort of your web browser.</b>
|
||||
<b>a Secure Scuttlebutt decentralized social network client</b>
|
||||
</h1>
|
||||
<p>
|
||||
Tilde Friends is a platform for building, running, and sharing web
|
||||
applications.
|
||||
</p>
|
||||
<p>
|
||||
Available for lots of devices:
|
||||
<i class="fa-brands fa-linux w3-xlarge"></i>
|
||||
<i class="fa-brands fa-android w3-xlarge"></i>
|
||||
<i class="fa-brands fa-apple w3-xlarge"></i>
|
||||
<i class="fa fa-mobile-screen w3-xlarge"></i>
|
||||
<i class="fa-brands fa-windows w3-xlarge"></i>
|
||||
In addition to participating in Secure Scuttlebutt, Tilde Friends is
|
||||
a platform for building, running, and sharing applications.
|
||||
</p>
|
||||
<a
|
||||
class="w3-button w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
>🦀 Try It</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://www.tildefriends.net/~cory/apps/"
|
||||
><i class="fa fa-link"></i> Try It</a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends"
|
||||
><i class="fa fa-mug-hot"></i> Development</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/"
|
||||
><i class="fa fa-mug-hot"></i> Development</a
|
||||
href="https://docs.tildefriends.net/"
|
||||
><i class="fa fa-book"></i> Documentation</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-black w3-padding-large"
|
||||
href="https://www.tildefriends.net/~cory/tildeblog/"
|
||||
><i class="fa fa-solid fa-square-rss"></i> Blog</a
|
||||
>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0" /> Get it
|
||||
on F-Droid</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||
>
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||
>
|
||||
<img src="googleplay.svg" style="height: 2em; margin: 0" />
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-col l4 m6">
|
||||
<img src="tildefriends.png" class="w3-image w3-right w3-hide-small" />
|
||||
@ -114,8 +83,100 @@
|
||||
<a href="https://www.tildefriends.net/"
|
||||
>https://www.tildefriends.net/</a
|
||||
>.
|
||||
<div class="w3-cell-row">
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Mobile</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://f-droid.org/en/packages/com.unprompted.tildefriends.fdroid/"
|
||||
><img src="f-droid.svg" style="height: 2em; margin: 0" />
|
||||
Get it on F-Droid</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://play.google.com/store/apps/details?id=com.unprompted.tildefriends"
|
||||
>
|
||||
<img
|
||||
src="googleplay.svg"
|
||||
style="height: 2em; margin: 0"
|
||||
/>
|
||||
Get it on Google Play (Open Testing)
|
||||
</a>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray w3-margin-top"
|
||||
href="https://testflight.apple.com/join/tXxgtSpE"
|
||||
>
|
||||
<img src="ios.svg" style="height: 2em; margin: 0" />
|
||||
Get it on iOS (TestFlight)
|
||||
</a>
|
||||
</p>
|
||||
<p>Just launch the app.</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Web</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-blue w3-padding-large"
|
||||
href="https://www.tildefriends.net/~core/ssb/"
|
||||
>🦀 Try It</a
|
||||
>
|
||||
</p>
|
||||
<p>
|
||||
<a href="/login?return=/~core/intro"
|
||||
>Register an account with tildefriends.net</a
|
||||
>
|
||||
to take it for a spin right away.
|
||||
</p>
|
||||
</div>
|
||||
<div class="w3-container w3-cell">
|
||||
<h3>Desktop</h3>
|
||||
<p>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-black w3-padding-large"
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
><i class="fa fa-download"></i> Download</a
|
||||
>
|
||||
<a
|
||||
class="w3-button w3-round-large w3-padding w3-blue-gray"
|
||||
href="https://dev.tildefriends.net/releases/tildefriends-x86_64.AppImage"
|
||||
>
|
||||
<img src="appimage.svg" style="height: 2em; margin: 0" />
|
||||
Get Linux 64-bit AppImage
|
||||
</a>
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends is distributed as a single executable file (or
|
||||
source that you can
|
||||
<a href="http://dev.tildefriends.net">build yourself</a>)
|
||||
and stores all of its data in a single
|
||||
file(<code>db.sqlite</code>). You can generally download the
|
||||
latest executable from
|
||||
<a
|
||||
href="https://dev.tildefriends.net/cory/tildefriends/releases"
|
||||
>releases</a
|
||||
>
|
||||
for your platform, mark it as executable (<code
|
||||
>chmod +x tildefriends*</code
|
||||
>
|
||||
on macOS and Linux), and run. Run with <code>-h</code> to
|
||||
learn more.
|
||||
</p>
|
||||
<p>
|
||||
Tilde Friends will run in the console and provide a web
|
||||
interface at
|
||||
<a href="http://localhost:12345/">http://localhost:12345/</a
|
||||
>. You will have to register a username and password to sign
|
||||
into your instance.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<p>
|
||||
After a <a href="/~core/intro">brief introduction</a>, Tilde
|
||||
Friends will take you to the Secure Scuttlebutt social network
|
||||
app.
|
||||
</p>
|
||||
</li>
|
||||
<li>Create an account to identify yourself with that instance.</li>
|
||||
<li>
|
||||
Describe yourself in your profile in the <b>ssb</b> app. Give
|
||||
yourself a name and an avatar if you like.
|
||||
@ -149,11 +210,11 @@
|
||||
<!-- SSB Section -->
|
||||
<div class="w3-light-grey">
|
||||
<div class="w3-row-padding w3-padding-64">
|
||||
<div class="w3-col l4 m6 s4">
|
||||
<div class="w3-col l4 m6 s4 w3-center">
|
||||
<a href="https://scuttlebutt.nz/"
|
||||
><img
|
||||
class="w3-image w3-round-large"
|
||||
src="ssb.png"
|
||||
class="w3-image"
|
||||
src="hermietildefriends.svg"
|
||||
alt="Secure Scuttlebutt"
|
||||
/></a>
|
||||
</div>
|
||||
@ -223,16 +284,15 @@
|
||||
|
||||
<!-- Technlology Section -->
|
||||
<div class="w3-container w3-padding-64 w3-light-grey w3-center">
|
||||
<h1 class="w3-jumbo"><b>Boring Technology</b></h1>
|
||||
<h1 class="w3-jumbo"><b>Built the Old Fashioned Way</b></h1>
|
||||
<p>
|
||||
Tilde Friends is built using boring, trusted tech. Unless a better
|
||||
reason presents itself, it strives to use only simple and widely adopted
|
||||
dependencies in order to keep it easy to build for all sorts of
|
||||
platforms and maintainable for a very long time.
|
||||
Tilde Friends strives to use only simple and widely adopted dependencies
|
||||
in order to keep it easy to build for all sorts of platforms and
|
||||
maintainable for a very long time.
|
||||
</p>
|
||||
<p>
|
||||
Though of course for building Tilde Friends apps, you are free to use
|
||||
whatever fits.
|
||||
whatever fits on top.
|
||||
</p>
|
||||
|
||||
<div class="w3-row" style="margin-top: 64px">
|
||||
|
3
apps/welcome/ios.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="814" height="1000">
|
||||
<path d="M788.1 340.9c-5.8 4.5-108.2 62.2-108.2 190.5 0 148.4 130.3 200.9 134.2 202.2-.6 3.2-20.7 71.9-68.7 141.9-42.8 61.6-87.5 123.1-155.5 123.1s-85.5-39.5-164-39.5c-76.5 0-103.7 40.8-165.9 40.8s-105.6-57-155.5-127C46.7 790.7 0 663 0 541.8c0-194.4 126.4-297.5 250.8-297.5 66.1 0 121.2 43.4 162.7 43.4 39.5 0 101.1-46 176.3-46 28.5 0 130.9 2.6 198.3 99.2zm-234-181.5c31.1-36.9 53.1-88.1 53.1-139.3 0-7.1-.6-14.3-1.9-20.1-50.6 1.9-110.8 33.7-147.1 75.8-28.5 32.4-55.1 83.6-55.1 135.5 0 7.8 1.3 15.6 1.9 18.1 3.2.6 8.4 1.3 13.6 1.3 45.4 0 102.5-30.4 135.5-71.3z"/>
|
||||
</svg>
|
After Width: | Height: | Size: 660 B |
Before Width: | Height: | Size: 50 KiB |
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
42
apps/wiki/lit-all.min.js
vendored
115
core/app.js
@ -1,53 +1,26 @@
|
||||
import * as core from './core.js';
|
||||
|
||||
let g_next_id = 1;
|
||||
let g_calls = {};
|
||||
|
||||
let gSessionIndex = 0;
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
*/
|
||||
function makeSessionId() {
|
||||
return 'session_' + (gSessionIndex++).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @returns
|
||||
*/
|
||||
function App() {
|
||||
this._on_output = null;
|
||||
this._send_queue = [];
|
||||
this.calls = {};
|
||||
this._next_call_id = 1;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} callback
|
||||
*/
|
||||
App.prototype.readOutput = function (callback) {
|
||||
this._on_output = callback;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} api
|
||||
* @returns
|
||||
*/
|
||||
App.prototype.makeFunction = function (api) {
|
||||
let self = this;
|
||||
let result = function () {
|
||||
let id = g_next_id++;
|
||||
while (!id || g_calls[id]) {
|
||||
id = g_next_id++;
|
||||
let id = self._next_call_id++;
|
||||
while (!id || self.calls[id]) {
|
||||
id = self._next_call_id++;
|
||||
}
|
||||
let promise = new Promise(function (resolve, reject) {
|
||||
g_calls[id] = {resolve: resolve, reject: reject};
|
||||
self.calls[id] = {resolve: resolve, reject: reject};
|
||||
});
|
||||
let message = {
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
method: api[0],
|
||||
params: [...arguments],
|
||||
id: id,
|
||||
@ -59,10 +32,6 @@ App.prototype.makeFunction = function (api) {
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} message
|
||||
*/
|
||||
App.prototype.send = function (message) {
|
||||
if (this._send_queue) {
|
||||
if (this._on_output) {
|
||||
@ -77,13 +46,7 @@ App.prototype.send = function (message) {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} request
|
||||
* @param {*} response
|
||||
* @param {*} client
|
||||
*/
|
||||
async function socket(request, response, client) {
|
||||
exports.app_socket = async function socket(request, response) {
|
||||
let process;
|
||||
let options = {};
|
||||
let credentials = await httpd.auth_query(request.headers);
|
||||
@ -103,10 +66,16 @@ async function socket(request, response, client) {
|
||||
try {
|
||||
message = JSON.parse(event.data);
|
||||
} catch (error) {
|
||||
print('ERROR', error, event.data, event.data.length, event.opCode);
|
||||
print(
|
||||
'WebSocket error:',
|
||||
error,
|
||||
event.data,
|
||||
event.data.length,
|
||||
event.opCode
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (message.action == 'hello') {
|
||||
if (!process && message.action == 'hello') {
|
||||
let packageOwner;
|
||||
let packageName;
|
||||
let blobId;
|
||||
@ -123,7 +92,7 @@ async function socket(request, response, client) {
|
||||
if (!blobId) {
|
||||
response.send(
|
||||
JSON.stringify({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
method: 'error',
|
||||
params: [message.path + ' not found'],
|
||||
id: -1,
|
||||
@ -164,7 +133,7 @@ async function socket(request, response, client) {
|
||||
options.packageOwner = packageOwner;
|
||||
options.packageName = packageName;
|
||||
options.url = message.url;
|
||||
let sessionId = makeSessionId();
|
||||
let sessionId = 'session_' + (gSessionIndex++).toString();
|
||||
if (blobId) {
|
||||
if (message.edit_only) {
|
||||
response.send(
|
||||
@ -176,9 +145,24 @@ async function socket(request, response, client) {
|
||||
}
|
||||
}
|
||||
if (process) {
|
||||
process.app.readOutput(function (message) {
|
||||
process.client_api.tfrpc = function (message) {
|
||||
if (message.id) {
|
||||
let calls = process?.app?.calls;
|
||||
if (calls) {
|
||||
let call = calls[message.id];
|
||||
if (call) {
|
||||
if (message.error !== undefined) {
|
||||
call.reject(message.error);
|
||||
} else {
|
||||
call.resolve(message.result);
|
||||
}
|
||||
delete calls[message.id];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
process.app._on_output = (message) =>
|
||||
response.send(JSON.stringify(message), 0x1);
|
||||
});
|
||||
process.app.send();
|
||||
}
|
||||
|
||||
@ -207,26 +191,13 @@ async function socket(request, response, client) {
|
||||
if (process && process.timeout > 0) {
|
||||
setTimeout(ping, process.timeout);
|
||||
}
|
||||
} else if (message.action == 'resetPermission') {
|
||||
if (process) {
|
||||
process.resetPermission(message.permission);
|
||||
}
|
||||
} else if (message.action == 'setActiveIdentity') {
|
||||
process.setActiveIdentity(message.identity);
|
||||
} else if (message.action == 'createIdentity') {
|
||||
await process.createIdentity();
|
||||
} else if (message.message == 'tfrpc') {
|
||||
if (message.id && g_calls[message.id]) {
|
||||
if (message.error !== undefined) {
|
||||
g_calls[message.id].reject(message.error);
|
||||
} else {
|
||||
g_calls[message.id].resolve(message.result);
|
||||
}
|
||||
delete g_calls[message.id];
|
||||
}
|
||||
} else {
|
||||
if (process && process.eventHandlers['message']) {
|
||||
await core.invoke(process.eventHandlers['message'], [message]);
|
||||
if (process) {
|
||||
if (process.client_api[message.action]) {
|
||||
process.client_api[message.action](message);
|
||||
} else if (process.eventHandlers['message']) {
|
||||
await core.invoke(process.eventHandlers['message'], [message]);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (event.opCode == 0x8) {
|
||||
@ -245,6 +216,6 @@ async function socket(request, response, client) {
|
||||
};
|
||||
|
||||
response.upgrade(100, {});
|
||||
}
|
||||
};
|
||||
|
||||
export {socket, App};
|
||||
export {App};
|
||||
|
@ -8,6 +8,7 @@ let gFiles = {};
|
||||
let gApp = {files: {}, emoji: '📦'};
|
||||
let gEditor;
|
||||
let gOriginalInput;
|
||||
let gUnloading;
|
||||
|
||||
let kErrorColor = '#dc322f';
|
||||
let kDisconnectColor = '#f00';
|
||||
@ -266,6 +267,7 @@ class TfNavigationElement extends LitElement {
|
||||
<button
|
||||
@click=${() => this.reset_permission(key)}
|
||||
class="w3-button w3-red"
|
||||
id=${'permission_reset:' + key}
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
@ -275,6 +277,7 @@ class TfNavigationElement extends LitElement {
|
||||
<button
|
||||
@click=${() => (this.show_permissions = false)}
|
||||
class="w3-button w3-blue"
|
||||
id="permissions_close"
|
||||
>
|
||||
Close
|
||||
</button>
|
||||
@ -409,6 +412,7 @@ class TfFilesElement extends LitElement {
|
||||
current: {type: String},
|
||||
files: {type: Object},
|
||||
dropping: {type: Number},
|
||||
drop_target: {type: String},
|
||||
};
|
||||
}
|
||||
|
||||
@ -447,6 +451,9 @@ class TfFilesElement extends LitElement {
|
||||
if (!this.files[file].clean) {
|
||||
classes.push('dirty');
|
||||
}
|
||||
if (this.drop_target == file) {
|
||||
classes.push('drop');
|
||||
}
|
||||
return html`<div
|
||||
class="${classes.join(' ')}"
|
||||
@click=${(x) => this.file_click(file)}
|
||||
@ -463,11 +470,12 @@ class TfFilesElement extends LitElement {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
this.dropping = 0;
|
||||
this.drop_target = undefined;
|
||||
for (let file of event.dataTransfer.files) {
|
||||
let buffer = await file.arrayBuffer();
|
||||
let text = new TextDecoder('latin1').decode(buffer);
|
||||
gFiles[file.name] = {
|
||||
doc: new cm6.EditorState.create({
|
||||
doc: cm6.EditorState.create({
|
||||
doc: text,
|
||||
extensions: cm6.extensions,
|
||||
}),
|
||||
@ -486,6 +494,7 @@ class TfFilesElement extends LitElement {
|
||||
*/
|
||||
drag_enter(event) {
|
||||
this.dropping++;
|
||||
this.drop_target = event.srcElement.innerText.trim();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
@ -495,6 +504,13 @@ class TfFilesElement extends LitElement {
|
||||
*/
|
||||
drag_leave(event) {
|
||||
this.dropping--;
|
||||
if (this.dropping == 0) {
|
||||
this.drop_target = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
drag_over(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -521,6 +537,10 @@ class TfFilesElement extends LitElement {
|
||||
background-color: #2aa198;
|
||||
}
|
||||
|
||||
div.file.drop {
|
||||
border: 4px solid red;
|
||||
}
|
||||
|
||||
div.file.dirty::after {
|
||||
content: '*';
|
||||
}
|
||||
@ -529,20 +549,12 @@ class TfFilesElement extends LitElement {
|
||||
@drop=${this.drop}
|
||||
@dragenter=${this.drag_enter}
|
||||
@dragleave=${this.drag_leave}
|
||||
@dragover=${this.drag_over}
|
||||
>
|
||||
${Object.keys(this.files)
|
||||
.sort()
|
||||
.map((x) => self.render_file(x))}
|
||||
</div>
|
||||
<div
|
||||
?hidden=${this.dropping == 0}
|
||||
@drop=${this.drop}
|
||||
@dragenter=${this.drag_enter}
|
||||
@dragleave=${this.drag_leave}
|
||||
style="text-align: center; vertical-align: middle; outline: 16px solid red; margin: -8px; background-color: rgba(255, 0, 0, 0.5); position: absolute; left: 16px; top: 16px; width: calc(100% - 16px); height: calc(100% - 16px); z-index: 1000"
|
||||
>
|
||||
Drop File(s)
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -1325,7 +1337,7 @@ function _receive_websocket_message(message) {
|
||||
line.append(key, message.stats[key]);
|
||||
}
|
||||
}
|
||||
} else if (message && message.message === 'tfrpc' && message.method) {
|
||||
} else if (message && message.action === 'tfrpc' && message.method) {
|
||||
let api = k_api[message.method];
|
||||
let id = message.id;
|
||||
let params = message.params;
|
||||
@ -1333,14 +1345,14 @@ function _receive_websocket_message(message) {
|
||||
Promise.resolve(api.func(...params))
|
||||
.then(function (result) {
|
||||
send({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
id: id,
|
||||
result: result,
|
||||
});
|
||||
})
|
||||
.catch(function (error) {
|
||||
send({
|
||||
message: 'tfrpc',
|
||||
action: 'tfrpc',
|
||||
id: id,
|
||||
error: error,
|
||||
});
|
||||
@ -1549,27 +1561,31 @@ function connectSocket(path) {
|
||||
_receive_websocket_message(JSON.parse(event.data));
|
||||
};
|
||||
gSocket.onclose = function (event) {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
if (gUnloading) {
|
||||
setStatusMessage('⚪ Closing...', kStatusColor);
|
||||
} else {
|
||||
const k_codes = {
|
||||
1000: 'Normal closure',
|
||||
1001: 'Going away',
|
||||
1002: 'Protocol error',
|
||||
1003: 'Unsupported data',
|
||||
1005: 'No status received',
|
||||
1006: 'Abnormal closure',
|
||||
1007: 'Invalid frame payload data',
|
||||
1008: 'Policy violation',
|
||||
1009: 'Message too big',
|
||||
1010: 'Missing extension',
|
||||
1011: 'Internal error',
|
||||
1012: 'Service restart',
|
||||
1013: 'Try again later',
|
||||
1014: 'Bad gateway',
|
||||
1015: 'TLS handshake',
|
||||
};
|
||||
setStatusMessage(
|
||||
'🔴 Closed: ' + (k_codes[event.code] || event.code),
|
||||
kDisconnectColor
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@ -1843,6 +1859,9 @@ window.addEventListener('load', function () {
|
||||
window.addEventListener('blur', blur);
|
||||
window.addEventListener('message', message, false);
|
||||
window.addEventListener('online', connectSocket);
|
||||
window.addEventListener('beforeunload', function () {
|
||||
gUnloading = true;
|
||||
});
|
||||
document.getElementById('name').value = window.location.pathname;
|
||||
document
|
||||
.getElementById('closeEditor')
|
||||
|
181
core/core.js
@ -1,34 +1,24 @@
|
||||
import * as app from './app.js';
|
||||
import * as form from './form.js';
|
||||
import * as http from './http.js';
|
||||
|
||||
let gProcesses = {};
|
||||
let gStatsTimer = false;
|
||||
let kPingInterval = 60 * 1000;
|
||||
let g_handler_index = 0;
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} out
|
||||
* @param {*} error
|
||||
*/
|
||||
function printError(out, error) {
|
||||
const k_ping_interval = 60 * 1000;
|
||||
|
||||
function printError(error) {
|
||||
if (error.stackTrace) {
|
||||
out.print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
|
||||
out.print(error.stackTrace);
|
||||
print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
|
||||
print(error.stackTrace);
|
||||
} else {
|
||||
for (let [k, v] of Object.entries(error)) {
|
||||
out.print(k, v);
|
||||
print(k, v);
|
||||
}
|
||||
out.print(error.toString());
|
||||
print(error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} handlers
|
||||
* @param {*} argv
|
||||
* @returns
|
||||
*/
|
||||
function invoke(handlers, argv) {
|
||||
let promises = [];
|
||||
if (handlers) {
|
||||
@ -49,12 +39,6 @@ function invoke(handlers, argv) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} eventName
|
||||
* @param {*} argv
|
||||
* @returns
|
||||
*/
|
||||
function broadcastEvent(eventName, argv) {
|
||||
let promises = [];
|
||||
for (let process of Object.values(gProcesses)) {
|
||||
@ -65,11 +49,6 @@ function broadcastEvent(eventName, argv) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
function broadcast(message) {
|
||||
let sender = this;
|
||||
let promises = [];
|
||||
@ -86,12 +65,6 @@ function broadcast(message) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {String} eventName
|
||||
* @param {*} argv
|
||||
* @returns
|
||||
*/
|
||||
function broadcastAppEventToUser(
|
||||
user,
|
||||
packageOwner,
|
||||
@ -114,12 +87,6 @@ function broadcastAppEventToUser(
|
||||
return Promise.all(promises);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} caller
|
||||
* @param {*} process
|
||||
* @returns
|
||||
*/
|
||||
function getUser(caller, process) {
|
||||
return {
|
||||
key: process.key,
|
||||
@ -130,12 +97,6 @@ function getUser(caller, process) {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} user
|
||||
* @param {*} process
|
||||
* @returns
|
||||
*/
|
||||
async function getApps(user, process) {
|
||||
if (
|
||||
process.credentials &&
|
||||
@ -162,28 +123,13 @@ async function getApps(user, process) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} from
|
||||
* @param {*} to
|
||||
* @param {*} message
|
||||
* @returns
|
||||
*/
|
||||
function postMessageInternal(from, to, message) {
|
||||
if (to.eventHandlers['message']) {
|
||||
return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} blobId
|
||||
* @param {*} key
|
||||
* @param {*} options
|
||||
* @returns
|
||||
*/
|
||||
async function getProcessBlob(blobId, key, options) {
|
||||
// TODO(tasiaiso): break this down ?
|
||||
let process = gProcesses[key];
|
||||
if (!process && !(options && 'create' in options && !options.create)) {
|
||||
let resolveReady;
|
||||
@ -202,7 +148,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
}
|
||||
process.lastActive = Date.now();
|
||||
process.lastPing = null;
|
||||
process.timeout = kPingInterval;
|
||||
process.timeout = k_ping_interval;
|
||||
process.ready = new Promise(function (resolve, reject) {
|
||||
resolveReady = resolve;
|
||||
rejectReady = reject;
|
||||
@ -462,16 +408,15 @@ async function getProcessBlob(blobId, key, options) {
|
||||
if (process.app) {
|
||||
process.app.makeFunction(['error'])(error);
|
||||
} else {
|
||||
printError({print: print}, error);
|
||||
printError(error);
|
||||
}
|
||||
} catch (e) {
|
||||
printError({print: print}, error);
|
||||
printError(error);
|
||||
}
|
||||
};
|
||||
imports.ssb = Object.fromEntries(
|
||||
Object.keys(ssb).map((key) => [key, ssb[key].bind(ssb)])
|
||||
);
|
||||
imports.ssb.port = tildefriends.ssb_port;
|
||||
imports.ssb.createIdentity = () => process.createIdentity();
|
||||
imports.ssb.addIdentity = function (id) {
|
||||
if (
|
||||
@ -651,17 +596,26 @@ async function getProcessBlob(blobId, key, options) {
|
||||
permissions: await imports.core.permissionsGranted(),
|
||||
});
|
||||
};
|
||||
process.resetPermission = async function resetPermission(permission) {
|
||||
let user = process?.credentials?.session?.name;
|
||||
await ssb.setUserPermission(
|
||||
user,
|
||||
options?.packageOwner,
|
||||
options?.packageName,
|
||||
permission,
|
||||
undefined
|
||||
);
|
||||
return process.sendPermissions();
|
||||
process.client_api = {
|
||||
createIdentity: function () {
|
||||
return process.createIdentity();
|
||||
},
|
||||
resetPermission: async function resetPermission(message) {
|
||||
let user = process?.credentials?.session?.name;
|
||||
await ssb.setUserPermission(
|
||||
user,
|
||||
options?.packageOwner,
|
||||
options?.packageName,
|
||||
message.permission,
|
||||
undefined
|
||||
);
|
||||
return process.sendPermissions();
|
||||
},
|
||||
setActiveIdentity: function setActiveIdentity(message) {
|
||||
return process.setActiveIdentity(message.identity);
|
||||
},
|
||||
};
|
||||
ssb.registerImports(imports, process);
|
||||
process.task.setImports(imports);
|
||||
process.task.activate();
|
||||
let source = await ssb.blobGet(blobId);
|
||||
@ -688,7 +642,7 @@ async function getProcessBlob(blobId, key, options) {
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
printError({print: print}, e);
|
||||
printError(e);
|
||||
}
|
||||
broadcastEvent('onSessionBegin', [getUser(process, process)]);
|
||||
if (process.app) {
|
||||
@ -702,14 +656,10 @@ async function getProcessBlob(blobId, key, options) {
|
||||
sendStats();
|
||||
}
|
||||
} catch (error) {
|
||||
if (process.app) {
|
||||
if (process?.task?.onError) {
|
||||
process.task.onError(error);
|
||||
} else {
|
||||
printError({print: print}, error);
|
||||
}
|
||||
if (process?.app && process?.task?.onError) {
|
||||
process.task.onError(error);
|
||||
} else {
|
||||
printError({print: print}, error);
|
||||
printError(error);
|
||||
}
|
||||
rejectReady(error);
|
||||
}
|
||||
@ -729,9 +679,6 @@ ssb.addEventListener('connections', function () {
|
||||
broadcastEvent('onConnectionsChanged', []);
|
||||
});
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
*/
|
||||
async function loadSettings() {
|
||||
let data = {};
|
||||
try {
|
||||
@ -750,9 +697,6 @@ async function loadSettings() {
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
*/
|
||||
function sendStats() {
|
||||
let apps = Object.values(gProcesses)
|
||||
.filter((process) => process.app)
|
||||
@ -768,8 +712,6 @@ function sendStats() {
|
||||
}
|
||||
}
|
||||
|
||||
let g_handler_index = 0;
|
||||
|
||||
exports.callAppHandler = async function callAppHandler(
|
||||
response,
|
||||
app_blob_id,
|
||||
@ -835,59 +777,4 @@ exports.callAppHandler = async function callAppHandler(
|
||||
response.end(answer?.data);
|
||||
};
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
*/
|
||||
loadSettings()
|
||||
.then(function (settings) {
|
||||
if (tildefriends.https_port && settings.http_redirect) {
|
||||
httpd.set_http_redirect(settings.http_redirect);
|
||||
}
|
||||
httpd.all('/app/socket', app.socket);
|
||||
let port = httpd.start(tildefriends.http_port);
|
||||
if (tildefriends.args.out_http_port_file) {
|
||||
print('Writing the port file.');
|
||||
File.writeFile(
|
||||
tildefriends.args.out_http_port_file,
|
||||
port.toString() + '\n'
|
||||
)
|
||||
.then(function (r) {
|
||||
print(
|
||||
'Wrote the port file:',
|
||||
tildefriends.args.out_http_port_file,
|
||||
r
|
||||
);
|
||||
})
|
||||
.catch(function () {
|
||||
print('Failed to write the port file.');
|
||||
});
|
||||
}
|
||||
|
||||
if (tildefriends.https_port) {
|
||||
async function start_tls() {
|
||||
const kCertificatePath = 'data/httpd/certificate.pem';
|
||||
const kPrivateKeyPath = 'data/httpd/privatekey.pem';
|
||||
let privateKey;
|
||||
let certificate;
|
||||
try {
|
||||
privateKey = utf8Decode(await File.readFile(kPrivateKeyPath));
|
||||
certificate = utf8Decode(await File.readFile(kCertificatePath));
|
||||
} catch (e) {
|
||||
print(`TLS disabled (${e.message}).`);
|
||||
return;
|
||||
}
|
||||
let context = new TlsContext();
|
||||
context.setPrivateKey(privateKey);
|
||||
context.setCertificate(certificate);
|
||||
httpd.start(tildefriends.https_port, context);
|
||||
}
|
||||
start_tls();
|
||||
}
|
||||
})
|
||||
.catch(function (error) {
|
||||
print('Failed to load settings.');
|
||||
printError({print: print}, error);
|
||||
exit(1);
|
||||
});
|
||||
|
||||
export {invoke, getProcessBlob};
|
||||
|
44
core/form.js
@ -1,44 +0,0 @@
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} encoded
|
||||
* @returns
|
||||
*/
|
||||
function decode(encoded) {
|
||||
let result = '';
|
||||
for (let i = 0; i < encoded.length; i++) {
|
||||
let c = encoded[i];
|
||||
if (c == '+') {
|
||||
result += ' ';
|
||||
} else if (c == '%') {
|
||||
result += String.fromCharCode(parseInt(encoded.slice(i + 1, i + 3), 16));
|
||||
i += 2;
|
||||
} else {
|
||||
result += c;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODOC
|
||||
* @param {*} encoded
|
||||
* @param {*} initial
|
||||
* @returns
|
||||
*/
|
||||
function decodeForm(encoded, initial) {
|
||||
let result = initial || {};
|
||||
if (encoded) {
|
||||
encoded = encoded.trim();
|
||||
let items = encoded.split('&');
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
let item = items[i];
|
||||
let equals = item.indexOf('=');
|
||||
let key = decode(item.slice(0, equals));
|
||||
let value = decode(item.slice(equals + 1));
|
||||
result[key] = value;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export {decodeForm};
|
20
core/w3.css
@ -1,4 +1,4 @@
|
||||
/* W3.CSS 4.15 December 2020 by Jan Egil and Borge Refsnes */
|
||||
/* W3.CSS 5.02 March 31 2025 by Jan Egil and Borge Refsnes */
|
||||
html{box-sizing:border-box}*,*:before,*:after{box-sizing:inherit}
|
||||
/* Extract from normalize.css by Nicolas Gallagher and Jonathan Neal git.io/normalize */
|
||||
html{-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}body{margin:0}
|
||||
@ -108,6 +108,8 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-round-small{border-radius:2px}.w3-round,.w3-round-medium{border-radius:4px}.w3-round-large{border-radius:8px}.w3-round-xlarge{border-radius:16px}.w3-round-xxlarge{border-radius:32px}
|
||||
.w3-row-padding,.w3-row-padding>.w3-half,.w3-row-padding>.w3-third,.w3-row-padding>.w3-twothird,.w3-row-padding>.w3-threequarter,.w3-row-padding>.w3-quarter,.w3-row-padding>.w3-col{padding:0 8px}
|
||||
.w3-container,.w3-panel{padding:0.01em 16px}.w3-panel{margin-top:16px;margin-bottom:16px}
|
||||
.w3-grid{display:grid}.w3-grid-padding{display:grid;gap:16px}.w3-flex{display:flex}
|
||||
.w3-text-center{text-align:center}.w3-text-bold,.w3-bold{font-weight:bold}.w3-text-italic,.w3-italic{font-style:italic}
|
||||
.w3-code,.w3-codespan{font-family:Consolas,"courier new";font-size:16px}
|
||||
.w3-code{width:auto;background-color:#fff;padding:8px 12px;border-left:4px solid #4CAF50;word-wrap:break-word}
|
||||
.w3-codespan{color:crimson;background-color:#f1f1f1;padding-left:4px;padding-right:4px;font-size:110%}
|
||||
@ -148,6 +150,7 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-button:hover{color:#000!important;background-color:#ccc!important}
|
||||
.w3-transparent,.w3-hover-none:hover{background-color:transparent!important}
|
||||
.w3-hover-none:hover{box-shadow:none!important}
|
||||
.w3-rtl{direction:rtl}.w3-ltr{direction:ltr}
|
||||
/* Colors */
|
||||
.w3-amber,.w3-hover-amber:hover{color:#000!important;background-color:#ffc107!important}
|
||||
.w3-aqua,.w3-hover-aqua:hover{color:#000!important;background-color:#00ffff!important}
|
||||
@ -175,6 +178,19 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-grey,.w3-hover-grey:hover,.w3-gray,.w3-hover-gray:hover{color:#000!important;background-color:#9e9e9e!important}
|
||||
.w3-light-grey,.w3-hover-light-grey:hover,.w3-light-gray,.w3-hover-light-gray:hover{color:#000!important;background-color:#f1f1f1!important}
|
||||
.w3-dark-grey,.w3-hover-dark-grey:hover,.w3-dark-gray,.w3-hover-dark-gray:hover{color:#fff!important;background-color:#616161!important}
|
||||
.w3-asphalt,.w3-hover-asphalt:hover{color:#fff!important;background-color:#343a40!important}
|
||||
.w3-crimson,.w3-hover-crimson:hover{color:#fff!important;background-color:#a20025!important}
|
||||
.w3-cobalt,w3-hover-cobalt:hover{color:#fff!important;background-color:#0050ef!important}
|
||||
.w3-emerald,.w3-hover-emerald:hover{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-olive,.w3-hover-olive:hover{color:#fff!important;background-color:#6d8764!important}
|
||||
.w3-paper,.w3-hover-paper:hover{color:#000!important;background-color:#f8f9fa!important}
|
||||
.w3-sienna,.w3-hover-sienna:hover{color:#fff!important;background-color:#a0522d!important}
|
||||
.w3-taupe,.w3-hover-taupe:hover{color:#fff!important;background-color:#87794e!important}
|
||||
.w3-danger{color:#fff!important;background-color:#dd0000!important}
|
||||
.w3-note{color:#000!important;background-color:#fff599!important}
|
||||
.w3-info{color:#fff!important;background-color:#0a6fc2!important}
|
||||
.w3-warning{color:#000!important;background-color:#ffb305!important}
|
||||
.w3-success{color:#fff!important;background-color:#008a00!important}
|
||||
.w3-pale-red,.w3-hover-pale-red:hover{color:#000!important;background-color:#ffdddd!important}
|
||||
.w3-pale-green,.w3-hover-pale-green:hover{color:#000!important;background-color:#ddffdd!important}
|
||||
.w3-pale-yellow,.w3-hover-pale-yellow:hover{color:#000!important;background-color:#ffffcc!important}
|
||||
@ -232,4 +248,4 @@ hr{border:0;border-top:1px solid #eee;margin:20px 0}
|
||||
.w3-border-light-grey,.w3-hover-border-light-grey:hover,.w3-border-light-gray,.w3-hover-border-light-gray:hover{border-color:#f1f1f1!important}
|
||||
.w3-border-dark-grey,.w3-hover-border-dark-grey:hover,.w3-border-dark-gray,.w3-hover-border-dark-gray:hover{border-color:#616161!important}
|
||||
.w3-border-pale-red,.w3-hover-border-pale-red:hover{border-color:#ffe7e7!important}.w3-border-pale-green,.w3-hover-border-pale-green:hover{border-color:#e7ffe7!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
.w3-border-pale-yellow,.w3-hover-border-pale-yellow:hover{border-color:#ffffcc!important}.w3-border-pale-blue,.w3-hover-border-pale-blue:hover{border-color:#e7ffff!important}
|
||||
|
13
default.nix
@ -1,4 +1,8 @@
|
||||
# How to upgrade to a newer version
|
||||
# - On the june and december release, you'll have to update nixpkgs to the current branch
|
||||
# Change `nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";`
|
||||
# to the latest release (see https://nixos.org/)
|
||||
# - Run `$ nix flake update`
|
||||
# - Comment `src.hash`
|
||||
# - Change `version`
|
||||
# - Run `$ nix build`
|
||||
@ -21,31 +25,32 @@
|
||||
}:
|
||||
pkgs.stdenv.mkDerivation rec {
|
||||
pname = "tildefriends";
|
||||
version = "0.0.27";
|
||||
version = "0.0.31";
|
||||
|
||||
src = pkgs.fetchFromGitea {
|
||||
domain = "dev.tildefriends.net";
|
||||
owner = "cory";
|
||||
repo = "tildefriends";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-NhoTBWYsWc206f1+M9haxHSJU6ZSGoP5nPStymAIyRk=";
|
||||
hash = "sha256-c2ZKVNikI5jN5GQuvp7S53qqnRZniSrJMF1FUZdVNPI=";
|
||||
fetchSubmodules = true;
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
bash
|
||||
glibc
|
||||
gnumake
|
||||
openssl
|
||||
which
|
||||
];
|
||||
|
||||
buildInputs = with pkgs; [
|
||||
glibc
|
||||
openssl
|
||||
which
|
||||
];
|
||||
|
||||
buildPhase = ''
|
||||
make -j $NIX_BUILD_CORES release
|
||||
make -j $NIX_BUILD_CORES release USE_SYSTEM_SSL=1
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
|
2
deps/c-ares
vendored
2
deps/codemirror/cm6.js
vendored
280
deps/codemirror_src/package-lock.json
generated
vendored
@ -19,9 +19,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/autocomplete": {
|
||||
"version": "6.18.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.4.tgz",
|
||||
"integrity": "sha512-sFAphGQIqyQZfP2ZBsSHV7xQvo9Py0rV0dW7W3IMRdS+zDuNb2l3no78CvUaWKGfzFjI4FTrLdUSj86IGb2hRA==",
|
||||
"version": "6.18.6",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.6.tgz",
|
||||
"integrity": "sha512-PHHBXFomUs5DF+9tCOM/UoW6XQ4R44lLNNhRaW9PKPTU0D7lIjRg3ElxaJnTwsl/oHiR93WSXDBrekhoUGCPtg==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@ -30,9 +30,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/commands": {
|
||||
"version": "6.8.0",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.0.tgz",
|
||||
"integrity": "sha512-q8VPEFaEP4ikSlt6ZxjB3zW72+7osfAYW9i8Zu943uqbKuz6utc1+F170hyLUCUltXORjQXRyYQNfkckzA/bPQ==",
|
||||
"version": "6.8.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
|
||||
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.4.0",
|
||||
@ -69,9 +69,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-javascript": {
|
||||
"version": "6.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.2.tgz",
|
||||
"integrity": "sha512-VGQfY+FCc285AhWuwjYxQyUQcYurWlxdKYT4bqwr3Twnd5wP5WSeu52t4tvvuWmljT4EmgEgZCqSieokhtY8hg==",
|
||||
"version": "6.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.2.4.tgz",
|
||||
"integrity": "sha512-0WVmhp1QOqZ4Rt6GlVGwKJN3KW7Xh4H2q8ZZNGZaP6lRdxXJzmjm4FqvmOojVj6khWJHIb9sp7U/72W7xQgqAA==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/language": "^6.6.0",
|
||||
@ -83,18 +83,18 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lang-json": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.1.tgz",
|
||||
"integrity": "sha512-+T1flHdgpqDDlJZ2Lkil/rLiRy684WMLc74xUnjJH48GQdfJo/pudlTRreZmKwzP8/tGdKf83wlbAdOCzlJOGQ==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lang-json/-/lang-json-6.0.2.tgz",
|
||||
"integrity": "sha512-x2OtO+AvwEHrEwR0FyyPtfDUiloG3rnVTSZV1W8UteaLL8/MajQd8DpvUb2YVzC+/T18aSDv0H9mu+xw0EStoQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@lezer/json": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/language": {
|
||||
"version": "6.10.8",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.8.tgz",
|
||||
"integrity": "sha512-wcP8XPPhDH2vTqf181U8MbZnW+tDyPYy0UzVOa+oHORjyT+mhhom9vBd7dApJwoDz9Nb/a8kHjJIsuA/t8vNFw==",
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.1.tgz",
|
||||
"integrity": "sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.23.0",
|
||||
@ -105,9 +105,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/lint": {
|
||||
"version": "6.8.4",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.4.tgz",
|
||||
"integrity": "sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==",
|
||||
"version": "6.8.5",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/lint/-/lint-6.8.5.tgz",
|
||||
"integrity": "sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.35.0",
|
||||
@ -115,9 +115,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/search": {
|
||||
"version": "6.5.8",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.8.tgz",
|
||||
"integrity": "sha512-PoWtZvo7c1XFeZWmmyaOp2G0XVbOnm+fJzvghqGAktBW3cufwJUWvSCcNG0ppXiBEM05mZu6RhMtXPv2hpllig==",
|
||||
"version": "6.5.11",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/search/-/search-6.5.11.tgz",
|
||||
"integrity": "sha512-KmWepDE6jUdL6n8cAAqIpRmLPBZ5ZKnicE8oGU/s3QrAVID+0VhLFrzUucVKHG5035/BSykhExDL/Xm7dHthiA==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.0.0",
|
||||
"@codemirror/view": "^6.0.0",
|
||||
@ -125,17 +125,17 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/state": {
|
||||
"version": "6.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.1.tgz",
|
||||
"integrity": "sha512-3rA9lcwciEB47ZevqvD8qgbzhM9qMb8vCcQCNmDfVRPQG4JT9mSb0Jg8H7YjKGGQcFnLN323fj9jdnG59Kx6bg==",
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
|
||||
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
|
||||
"dependencies": {
|
||||
"@marijn/find-cluster-break": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/theme-one-dark": {
|
||||
"version": "6.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.2.tgz",
|
||||
"integrity": "sha512-F+sH0X16j/qFLMAfbciKTxVOwkdAS336b7AXTKOZhy8BR3eH/RelsnLgLFINrpST63mmN2OuwUt0W2ndUgYwUA==",
|
||||
"version": "6.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.3.tgz",
|
||||
"integrity": "sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "^6.0.0",
|
||||
"@codemirror/state": "^6.0.0",
|
||||
@ -144,11 +144,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codemirror/view": {
|
||||
"version": "6.36.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.2.tgz",
|
||||
"integrity": "sha512-DZ6ONbs8qdJK0fdN7AB82CgI6tYXf4HWk1wSVa0+9bhVznCuuvhQtX8bFBoy3dv8rZSQqUd8GvhVAcielcidrA==",
|
||||
"version": "6.37.2",
|
||||
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.37.2.tgz",
|
||||
"integrity": "sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==",
|
||||
"dependencies": {
|
||||
"@codemirror/state": "^6.5.0",
|
||||
"crelt": "^1.0.6",
|
||||
"style-mod": "^4.1.0",
|
||||
"w3c-keyname": "^2.2.4"
|
||||
}
|
||||
@ -217,13 +218,13 @@
|
||||
"integrity": "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="
|
||||
},
|
||||
"node_modules/@lezer/css": {
|
||||
"version": "1.1.10",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.10.tgz",
|
||||
"integrity": "sha512-V5/89eDapjeAkWPBpWEfQjZ1Hag3aYUUJOL8213X0dFRuXJ4BXa5NKl9USzOnaLod4AOpmVCkduir2oKwZYZtg==",
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.2.1.tgz",
|
||||
"integrity": "sha512-2F5tOqzKEKbCUNraIXc0f6HKeyKlmMWJnBB0i4XW6dJgssrZO/YlZ2pY5xgyqDleqqhiNJ3dQhbrV2aClZQMvg==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.0.0",
|
||||
"@lezer/lr": "^1.0.0"
|
||||
"@lezer/lr": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/highlight": {
|
||||
@ -245,9 +246,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@lezer/javascript": {
|
||||
"version": "1.4.21",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.21.tgz",
|
||||
"integrity": "sha512-lL+1fcuxWYPURMM/oFZLEDm0XuLN128QPV+VuGtKpeaOGdcl9F2LYC3nh1S9LkPqx9M0mndZFdXCipNAZpzIkQ==",
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.5.1.tgz",
|
||||
"integrity": "sha512-ATOImjeVJuvgm3JQ/bpo2Tmv55HSScE2MTPnKRMRIPx2cLhHGyX2VnqpHhtIV1tVzIjZDbcWQm+NCTF40ggZVw==",
|
||||
"dependencies": {
|
||||
"@lezer/common": "^1.2.0",
|
||||
"@lezer/highlight": "^1.1.3",
|
||||
@ -323,9 +324,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/pluginutils": {
|
||||
"version": "5.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.1.4.tgz",
|
||||
"integrity": "sha512-USm05zrsFxYLPdWWq+K3STlWiT/3ELn3RcV5hJMghpeAIhxfsUIg6mt12CBJBInWMV4VneoV7SfGv8xIwo2qNQ==",
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.2.0.tgz",
|
||||
"integrity": "sha512-qWJ2ZTbmumwiLFomfzTyt5Kng4hwPi9rwCYN4SHb6eaRU1KNO4ccxINHr/VhH4GgPlt1XfSTLX2LBTme8ne4Zw==",
|
||||
"dependencies": {
|
||||
"@types/estree": "^1.0.0",
|
||||
"estree-walker": "^2.0.2",
|
||||
@ -344,9 +345,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm-eabi": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.32.1.tgz",
|
||||
"integrity": "sha512-/pqA4DmqyCm8u5YIDzIdlLcEmuvxb0v8fZdFhVMszSpDTgbQKdw3/mB3eMUHIbubtJ6F9j+LtmyCnHTEqIHyzA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.44.0.tgz",
|
||||
"integrity": "sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -356,9 +357,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-android-arm64": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.32.1.tgz",
|
||||
"integrity": "sha512-If3PDskT77q7zgqVqYuj7WG3WC08G1kwXGVFi9Jr8nY6eHucREHkfpX79c0ACAjLj3QIWKPJR7w4i+f5EdLH5Q==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -368,9 +369,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-arm64": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.32.1.tgz",
|
||||
"integrity": "sha512-zCpKHioQ9KgZToFp5Wvz6zaWbMzYQ2LJHQ+QixDKq52KKrF65ueu6Af4hLlLWHjX1Wf/0G5kSJM9PySW9IrvHA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -380,9 +381,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-darwin-x64": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.32.1.tgz",
|
||||
"integrity": "sha512-sFvF+t2+TyUo/ZQqUcifrJIgznx58oFZbdHS9TvHq3xhPVL9nOp+yZ6LKrO9GWTP+6DbFtoyLDbjTpR62Mbr3Q==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.44.0.tgz",
|
||||
"integrity": "sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -392,9 +393,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-arm64": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.32.1.tgz",
|
||||
"integrity": "sha512-NbOa+7InvMWRcY9RG+B6kKIMD/FsnQPH0MWUvDlQB1iXnF/UcKSudCXZtv4lW+C276g3w5AxPbfry5rSYvyeYA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.44.0.tgz",
|
||||
"integrity": "sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -404,9 +405,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-freebsd-x64": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.32.1.tgz",
|
||||
"integrity": "sha512-JRBRmwvHPXR881j2xjry8HZ86wIPK2CcDw0EXchE1UgU0ubWp9nvlT7cZYKc6bkypBt745b4bglf3+xJ7hXWWw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.44.0.tgz",
|
||||
"integrity": "sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -416,9 +417,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.32.1.tgz",
|
||||
"integrity": "sha512-PKvszb+9o/vVdUzCCjL0sKHukEQV39tD3fepXxYrHE3sTKrRdCydI7uldRLbjLmDA3TFDmh418XH19NOsDRH8g==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.44.0.tgz",
|
||||
"integrity": "sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -428,9 +429,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.32.1.tgz",
|
||||
"integrity": "sha512-9WHEMV6Y89eL606ReYowXuGF1Yb2vwfKWKdD1A5h+OYnPZSJvxbEjxTRKPgi7tkP2DSnW0YLab1ooy+i/FQp/Q==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.44.0.tgz",
|
||||
"integrity": "sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
@ -440,9 +441,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-tZWc9iEt5fGJ1CL2LRPw8OttkCBDs+D8D3oEM8mH8S1ICZCtFJhD7DZ3XMGM8kpqHvhGUTvNUYVDnmkj4BDXnw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -452,9 +453,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-arm64-musl": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.32.1.tgz",
|
||||
"integrity": "sha512-FTYc2YoTWUsBz5GTTgGkRYYJ5NGJIi/rCY4oK/I8aKowx1ToXeoVVbIE4LGAjsauvlhjfl0MYacxClLld1VrOw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -464,9 +465,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-F51qLdOtpS6P1zJVRzYM0v6MrBNypyPEN1GfMiz0gPu9jN8ScGaEFIZQwteSsGKg799oR5EaP7+B2jHgL+d+Kw==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
@ -476,9 +477,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-wO0WkfSppfX4YFm5KhdCCpnpGbtgQNj/tgvYzrVYFKDpven8w2N6Gg5nB6w+wAMO3AIfSTWeTjfVe+uZ23zAlg==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
@ -488,9 +489,21 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-iWswS9cIXfJO1MFYtI/4jjlrGb/V58oMu4dYJIKnR5UIwbkzR0PJ09O0PDZT0oJ3LYWXBSWahNf/Mjo6i1E5/g==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-riscv64-musl": {
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
@ -500,9 +513,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-s390x-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-RKt8NI9tebzmEthMnfVgG3i/XeECkMPS+ibVZjZ6mNekpbbUmkNWuIN2yHsb/mBPyZke4nlI4YqIdFPgKuoyQQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
@ -512,9 +525,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-gnu": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.32.1.tgz",
|
||||
"integrity": "sha512-WQFLZ9c42ECqEjwg/GHHsouij3pzLXkFdz0UxHa/0OM12LzvX7DzedlY0SIEly2v18YZLRhCRoHZDxbBSWoGYg==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.44.0.tgz",
|
||||
"integrity": "sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -524,9 +537,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-linux-x64-musl": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.32.1.tgz",
|
||||
"integrity": "sha512-BLoiyHDOWoS3uccNSADMza6V6vCNiphi94tQlVIL5de+r6r/CCQuNnerf+1g2mnk2b6edp5dk0nhdZ7aEjOBsA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.44.0.tgz",
|
||||
"integrity": "sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -536,9 +549,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-arm64-msvc": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.32.1.tgz",
|
||||
"integrity": "sha512-w2l3UnlgYTNNU+Z6wOR8YdaioqfEnwPjIsJ66KxKAf0p+AuL2FHeTX6qvM+p/Ue3XPBVNyVSfCrfZiQh7vZHLQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
@ -548,9 +561,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-ia32-msvc": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.32.1.tgz",
|
||||
"integrity": "sha512-Am9H+TGLomPGkBnaPWie4F3x+yQ2rr4Bk2jpwy+iV+Gel9jLAu/KqT8k3X4jxFPW6Zf8OMnehyutsd+eHoq1WQ==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
@ -560,9 +573,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@rollup/rollup-win32-x64-msvc": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.32.1.tgz",
|
||||
"integrity": "sha512-ar80GhdZb4DgmW3myIS9nRFYcpJRSME8iqWgzH2i44u+IdrzmiXVxeFnExQ5v4JYUSpg94bWjevMG8JHf1Da5Q==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.44.0.tgz",
|
||||
"integrity": "sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
@ -572,9 +585,9 @@
|
||||
]
|
||||
},
|
||||
"node_modules/@types/estree": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
|
||||
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw=="
|
||||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
|
||||
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="
|
||||
},
|
||||
"node_modules/@types/resolve": {
|
||||
"version": "1.20.2",
|
||||
@ -582,9 +595,9 @@
|
||||
"integrity": "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="
|
||||
},
|
||||
"node_modules/acorn": {
|
||||
"version": "8.14.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
|
||||
"integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
|
||||
"version": "8.15.0",
|
||||
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
@ -600,9 +613,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/codemirror": {
|
||||
"version": "6.0.1",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz",
|
||||
"integrity": "sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==",
|
||||
"version": "6.0.2",
|
||||
"resolved": "https://registry.npmjs.org/codemirror/-/codemirror-6.0.2.tgz",
|
||||
"integrity": "sha512-VhydHotNW5w1UGK0Qj96BwSk/Zqbp9WbnyK2W/eVMv4QyF41INRGpjUhFJY7/uDNuudSc33a/PKr4iDqRduvHw==",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.0.0",
|
||||
"@codemirror/commands": "^6.0.0",
|
||||
@ -733,11 +746,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/rollup": {
|
||||
"version": "4.32.1",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.32.1.tgz",
|
||||
"integrity": "sha512-z+aeEsOeEa3mEbS1Tjl6sAZ8NE3+AalQz1RJGj81M+fizusbdDMoEJwdJNHfaB40Scr4qNu+welOfes7maKonA==",
|
||||
"version": "4.44.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.44.0.tgz",
|
||||
"integrity": "sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==",
|
||||
"dependencies": {
|
||||
"@types/estree": "1.0.6"
|
||||
"@types/estree": "1.0.8"
|
||||
},
|
||||
"bin": {
|
||||
"rollup": "dist/bin/rollup"
|
||||
@ -747,25 +760,26 @@
|
||||
"npm": ">=8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@rollup/rollup-android-arm-eabi": "4.32.1",
|
||||
"@rollup/rollup-android-arm64": "4.32.1",
|
||||
"@rollup/rollup-darwin-arm64": "4.32.1",
|
||||
"@rollup/rollup-darwin-x64": "4.32.1",
|
||||
"@rollup/rollup-freebsd-arm64": "4.32.1",
|
||||
"@rollup/rollup-freebsd-x64": "4.32.1",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.32.1",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.32.1",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.32.1",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.32.1",
|
||||
"@rollup/rollup-linux-x64-musl": "4.32.1",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.32.1",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.32.1",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.32.1",
|
||||
"@rollup/rollup-android-arm-eabi": "4.44.0",
|
||||
"@rollup/rollup-android-arm64": "4.44.0",
|
||||
"@rollup/rollup-darwin-arm64": "4.44.0",
|
||||
"@rollup/rollup-darwin-x64": "4.44.0",
|
||||
"@rollup/rollup-freebsd-arm64": "4.44.0",
|
||||
"@rollup/rollup-freebsd-x64": "4.44.0",
|
||||
"@rollup/rollup-linux-arm-gnueabihf": "4.44.0",
|
||||
"@rollup/rollup-linux-arm-musleabihf": "4.44.0",
|
||||
"@rollup/rollup-linux-arm64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-arm64-musl": "4.44.0",
|
||||
"@rollup/rollup-linux-loongarch64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-powerpc64le-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-riscv64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-riscv64-musl": "4.44.0",
|
||||
"@rollup/rollup-linux-s390x-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-x64-gnu": "4.44.0",
|
||||
"@rollup/rollup-linux-x64-musl": "4.44.0",
|
||||
"@rollup/rollup-win32-arm64-msvc": "4.44.0",
|
||||
"@rollup/rollup-win32-ia32-msvc": "4.44.0",
|
||||
"@rollup/rollup-win32-x64-msvc": "4.44.0",
|
||||
"fsevents": "~2.3.2"
|
||||
}
|
||||
},
|
||||
@ -840,13 +854,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/terser": {
|
||||
"version": "5.37.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.37.0.tgz",
|
||||
"integrity": "sha512-B8wRRkmre4ERucLM/uXx4MOV5cbnOlVAqUst+1+iLKPI0dOgFO28f84ptoQt9HEI537PMzfYa/d+GEPKTRXmYA==",
|
||||
"version": "5.43.1",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||
"integrity": "sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@jridgewell/source-map": "^0.3.3",
|
||||
"acorn": "^8.8.2",
|
||||
"acorn": "^8.14.0",
|
||||
"commander": "^2.20.0",
|
||||
"source-map-support": "~0.5.20"
|
||||
},
|
||||
|
2
deps/libbacktrace
vendored
2
deps/libuv
vendored
42
deps/lit/lit-all.min.js
vendored
2
deps/lit/lit-all.min.js.map
vendored
2
deps/openssl_src
vendored
2
deps/quickjs
vendored
2
deps/speedscope/index.html
vendored
@ -11,7 +11,7 @@
|
||||
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
|
||||
</head>
|
||||
<body>
|
||||
<script src="speedscope-CAEVGCWN.js"></script>
|
||||
<script src="speedscope-VHEG2FVF.js"></script>
|
||||
|
||||
|
||||
|
||||
|
6
deps/speedscope/release.txt
vendored
@ -1,3 +1,3 @@
|
||||
speedscope@1.22.0
|
||||
Thu Jan 16 16:49:47 PST 2025
|
||||
bd7b44a0a7d63375ee6ea0a0d1b96e65a456642f
|
||||
speedscope@1.22.2
|
||||
Sat Feb 15 13:02:38 PST 2025
|
||||
1c254dcb3e2b4f6d921340d20e972d9d27b788f4
|
||||
|
1048
deps/sqlite/shell.c
vendored
8425
deps/sqlite/sqlite3.c
vendored
291
deps/sqlite/sqlite3.h
vendored
@ -133,7 +133,7 @@ extern "C" {
|
||||
**
|
||||
** Since [version 3.6.18] ([dateof:3.6.18]),
|
||||
** SQLite source code has been stored in the
|
||||
** <a href="http://www.fossil-scm.org/">Fossil configuration management
|
||||
** <a href="http://fossil-scm.org/">Fossil configuration management
|
||||
** system</a>. ^The SQLITE_SOURCE_ID macro evaluates to
|
||||
** a string which identifies a particular check-in of SQLite
|
||||
** within its configuration management system. ^The SQLITE_SOURCE_ID
|
||||
@ -146,9 +146,9 @@ extern "C" {
|
||||
** [sqlite3_libversion_number()], [sqlite3_sourceid()],
|
||||
** [sqlite_version()] and [sqlite_source_id()].
|
||||
*/
|
||||
#define SQLITE_VERSION "3.48.0"
|
||||
#define SQLITE_VERSION_NUMBER 3048000
|
||||
#define SQLITE_SOURCE_ID "2025-01-14 11:05:00 d2fe6b05f38d9d7cd78c5d252e99ac59f1aea071d669830c1ffe4e8966e84010"
|
||||
#define SQLITE_VERSION "3.50.1"
|
||||
#define SQLITE_VERSION_NUMBER 3050001
|
||||
#define SQLITE_SOURCE_ID "2025-06-06 14:52:32 b77dc5e0f596d2140d9ac682b2893ff65d3a4140aa86067a3efebe29dc914c95"
|
||||
|
||||
/*
|
||||
** CAPI3REF: Run-Time Library Version Numbers
|
||||
@ -1163,6 +1163,12 @@ struct sqlite3_io_methods {
|
||||
** the value that M is to be set to. Before returning, the 32-bit signed
|
||||
** integer is overwritten with the previous value of M.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_BLOCK_ON_CONNECT]]
|
||||
** The [SQLITE_FCNTL_BLOCK_ON_CONNECT] opcode is used to configure the
|
||||
** VFS to block when taking a SHARED lock to connect to a wal mode database.
|
||||
** This is used to implement the functionality associated with
|
||||
** SQLITE_SETLK_BLOCK_ON_CONNECT.
|
||||
**
|
||||
** <li>[[SQLITE_FCNTL_DATA_VERSION]]
|
||||
** The [SQLITE_FCNTL_DATA_VERSION] opcode is used to detect changes to
|
||||
** a database file. The argument is a pointer to a 32-bit unsigned integer.
|
||||
@ -1259,6 +1265,7 @@ struct sqlite3_io_methods {
|
||||
#define SQLITE_FCNTL_CKSM_FILE 41
|
||||
#define SQLITE_FCNTL_RESET_CACHE 42
|
||||
#define SQLITE_FCNTL_NULL_IO 43
|
||||
#define SQLITE_FCNTL_BLOCK_ON_CONNECT 44
|
||||
|
||||
/* deprecated names */
|
||||
#define SQLITE_GET_LOCKPROXYFILE SQLITE_FCNTL_GET_LOCKPROXYFILE
|
||||
@ -1989,13 +1996,16 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_CONFIG_LOOKASIDE]] <dt>SQLITE_CONFIG_LOOKASIDE</dt>
|
||||
** <dd> ^(The SQLITE_CONFIG_LOOKASIDE option takes two arguments that determine
|
||||
** the default size of lookaside memory on each [database connection].
|
||||
** the default size of [lookaside memory] on each [database connection].
|
||||
** The first argument is the
|
||||
** size of each lookaside buffer slot and the second is the number of
|
||||
** slots allocated to each database connection.)^ ^(SQLITE_CONFIG_LOOKASIDE
|
||||
** sets the <i>default</i> lookaside size. The [SQLITE_DBCONFIG_LOOKASIDE]
|
||||
** option to [sqlite3_db_config()] can be used to change the lookaside
|
||||
** configuration on individual connections.)^ </dd>
|
||||
** size of each lookaside buffer slot ("sz") and the second is the number of
|
||||
** slots allocated to each database connection ("cnt").)^
|
||||
** ^(SQLITE_CONFIG_LOOKASIDE sets the <i>default</i> lookaside size.
|
||||
** The [SQLITE_DBCONFIG_LOOKASIDE] option to [sqlite3_db_config()] can
|
||||
** be used to change the lookaside configuration on individual connections.)^
|
||||
** The [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to change the
|
||||
** default lookaside configuration at compile-time.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_CONFIG_PCACHE2]] <dt>SQLITE_CONFIG_PCACHE2</dt>
|
||||
** <dd> ^(The SQLITE_CONFIG_PCACHE2 option takes a single argument which is
|
||||
@ -2211,7 +2221,15 @@ struct sqlite3_mem_methods {
|
||||
** CAPI3REF: Database Connection Configuration Options
|
||||
**
|
||||
** These constants are the available integer configuration options that
|
||||
** can be passed as the second argument to the [sqlite3_db_config()] interface.
|
||||
** can be passed as the second parameter to the [sqlite3_db_config()] interface.
|
||||
**
|
||||
** The [sqlite3_db_config()] interface is a var-args functions. It takes a
|
||||
** variable number of parameters, though always at least two. The number of
|
||||
** parameters passed into sqlite3_db_config() depends on which of these
|
||||
** constants is given as the second parameter. This documentation page
|
||||
** refers to parameters beyond the second as "arguments". Thus, when this
|
||||
** page says "the N-th argument" it means "the N-th parameter past the
|
||||
** configuration option" or "the (N+2)-th parameter to sqlite3_db_config()".
|
||||
**
|
||||
** New configuration options may be added in future releases of SQLite.
|
||||
** Existing configuration options might be discontinued. Applications
|
||||
@ -2223,31 +2241,57 @@ struct sqlite3_mem_methods {
|
||||
** <dl>
|
||||
** [[SQLITE_DBCONFIG_LOOKASIDE]]
|
||||
** <dt>SQLITE_DBCONFIG_LOOKASIDE</dt>
|
||||
** <dd> ^This option takes three additional arguments that determine the
|
||||
** [lookaside memory allocator] configuration for the [database connection].
|
||||
** ^The first argument (the third parameter to [sqlite3_db_config()] is a
|
||||
** <dd> The SQLITE_DBCONFIG_LOOKASIDE option is used to adjust the
|
||||
** configuration of the [lookaside memory allocator] within a database
|
||||
** connection.
|
||||
** The arguments to the SQLITE_DBCONFIG_LOOKASIDE option are <i>not</i>
|
||||
** in the [DBCONFIG arguments|usual format].
|
||||
** The SQLITE_DBCONFIG_LOOKASIDE option takes three arguments, not two,
|
||||
** so that a call to [sqlite3_db_config()] that uses SQLITE_DBCONFIG_LOOKASIDE
|
||||
** should have a total of five parameters.
|
||||
** <ol>
|
||||
** <li><p>The first argument ("buf") is a
|
||||
** pointer to a memory buffer to use for lookaside memory.
|
||||
** ^The first argument after the SQLITE_DBCONFIG_LOOKASIDE verb
|
||||
** may be NULL in which case SQLite will allocate the
|
||||
** lookaside buffer itself using [sqlite3_malloc()]. ^The second argument is the
|
||||
** size of each lookaside buffer slot. ^The third argument is the number of
|
||||
** slots. The size of the buffer in the first argument must be greater than
|
||||
** or equal to the product of the second and third arguments. The buffer
|
||||
** must be aligned to an 8-byte boundary. ^If the second argument to
|
||||
** SQLITE_DBCONFIG_LOOKASIDE is not a multiple of 8, it is internally
|
||||
** rounded down to the next smaller multiple of 8. ^(The lookaside memory
|
||||
** The first argument may be NULL in which case SQLite will allocate the
|
||||
** lookaside buffer itself using [sqlite3_malloc()].
|
||||
** <li><P>The second argument ("sz") is the
|
||||
** size of each lookaside buffer slot. Lookaside is disabled if "sz"
|
||||
** is less than 8. The "sz" argument should be a multiple of 8 less than
|
||||
** 65536. If "sz" does not meet this constraint, it is reduced in size until
|
||||
** it does.
|
||||
** <li><p>The third argument ("cnt") is the number of slots. Lookaside is disabled
|
||||
** if "cnt"is less than 1. The "cnt" value will be reduced, if necessary, so
|
||||
** that the product of "sz" and "cnt" does not exceed 2,147,418,112. The "cnt"
|
||||
** parameter is usually chosen so that the product of "sz" and "cnt" is less
|
||||
** than 1,000,000.
|
||||
** </ol>
|
||||
** <p>If the "buf" argument is not NULL, then it must
|
||||
** point to a memory buffer with a size that is greater than
|
||||
** or equal to the product of "sz" and "cnt".
|
||||
** The buffer must be aligned to an 8-byte boundary.
|
||||
** The lookaside memory
|
||||
** configuration for a database connection can only be changed when that
|
||||
** connection is not currently using lookaside memory, or in other words
|
||||
** when the "current value" returned by
|
||||
** [sqlite3_db_status](D,[SQLITE_DBSTATUS_LOOKASIDE_USED],...) is zero.
|
||||
** when the value returned by [SQLITE_DBSTATUS_LOOKASIDE_USED] is zero.
|
||||
** Any attempt to change the lookaside memory configuration when lookaside
|
||||
** memory is in use leaves the configuration unchanged and returns
|
||||
** [SQLITE_BUSY].)^</dd>
|
||||
** [SQLITE_BUSY].
|
||||
** If the "buf" argument is NULL and an attempt
|
||||
** to allocate memory based on "sz" and "cnt" fails, then
|
||||
** lookaside is silently disabled.
|
||||
** <p>
|
||||
** The [SQLITE_CONFIG_LOOKASIDE] configuration option can be used to set the
|
||||
** default lookaside configuration at initialization. The
|
||||
** [-DSQLITE_DEFAULT_LOOKASIDE] option can be used to set the default lookaside
|
||||
** configuration at compile-time. Typical values for lookaside are 1200 for
|
||||
** "sz" and 40 to 100 for "cnt".
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_FKEY]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_FKEY</dt>
|
||||
** <dd> ^This option is used to enable or disable the enforcement of
|
||||
** [foreign key constraints]. There should be two additional arguments.
|
||||
** [foreign key constraints]. This is the same setting that is
|
||||
** enabled or disabled by the [PRAGMA foreign_keys] statement.
|
||||
** The first argument is an integer which is 0 to disable FK enforcement,
|
||||
** positive to enable FK enforcement or negative to leave FK enforcement
|
||||
** unchanged. The second parameter is a pointer to an integer into which
|
||||
@ -2269,13 +2313,13 @@ struct sqlite3_mem_methods {
|
||||
** <p>Originally this option disabled all triggers. ^(However, since
|
||||
** SQLite version 3.35.0, TEMP triggers are still allowed even if
|
||||
** this option is off. So, in other words, this option now only disables
|
||||
** triggers in the main database schema or in the schemas of ATTACH-ed
|
||||
** triggers in the main database schema or in the schemas of [ATTACH]-ed
|
||||
** databases.)^ </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_VIEW]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_VIEW</dt>
|
||||
** <dd> ^This option is used to enable or disable [CREATE VIEW | views].
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable views,
|
||||
** positive to enable views or negative to leave the setting unchanged.
|
||||
** The second parameter is a pointer to an integer into which
|
||||
@ -2294,7 +2338,7 @@ struct sqlite3_mem_methods {
|
||||
** <dd> ^This option is used to enable or disable the
|
||||
** [fts3_tokenizer()] function which is part of the
|
||||
** [FTS3] full-text search engine extension.
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** The first argument is an integer which is 0 to disable fts3_tokenizer() or
|
||||
** positive to enable fts3_tokenizer() or negative to leave the setting
|
||||
** unchanged.
|
||||
@ -2309,7 +2353,7 @@ struct sqlite3_mem_methods {
|
||||
** interface independently of the [load_extension()] SQL function.
|
||||
** The [sqlite3_enable_load_extension()] API enables or disables both the
|
||||
** C-API [sqlite3_load_extension()] and the SQL function [load_extension()].
|
||||
** There should be two additional arguments.
|
||||
** There must be two additional arguments.
|
||||
** When the first argument to this interface is 1, then only the C-API is
|
||||
** enabled and the SQL function remains disabled. If the first argument to
|
||||
** this interface is 0, then both the C-API and the SQL function are disabled.
|
||||
@ -2323,23 +2367,30 @@ struct sqlite3_mem_methods {
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_MAINDBNAME]] <dt>SQLITE_DBCONFIG_MAINDBNAME</dt>
|
||||
** <dd> ^This option is used to change the name of the "main" database
|
||||
** schema. ^The sole argument is a pointer to a constant UTF8 string
|
||||
** which will become the new schema name in place of "main". ^SQLite
|
||||
** does not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into this DBCONFIG option is unchanged
|
||||
** until after the database connection closes.
|
||||
** schema. This option does not follow the
|
||||
** [DBCONFIG arguments|usual SQLITE_DBCONFIG argument format].
|
||||
** This option takes exactly one additional argument so that the
|
||||
** [sqlite3_db_config()] call has a total of three parameters. The
|
||||
** extra argument must be a pointer to a constant UTF8 string which
|
||||
** will become the new schema name in place of "main". ^SQLite does
|
||||
** not make a copy of the new main schema name string, so the application
|
||||
** must ensure that the argument passed into SQLITE_DBCONFIG MAINDBNAME
|
||||
** is unchanged until after the database connection closes.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE]]
|
||||
** <dt>SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE</dt>
|
||||
** <dd> Usually, when a database in wal mode is closed or detached from a
|
||||
** database handle, SQLite checks if this will mean that there are now no
|
||||
** connections at all to the database. If so, it performs a checkpoint
|
||||
** operation before closing the connection. This option may be used to
|
||||
** override this behavior. The first parameter passed to this operation
|
||||
** is an integer - positive to disable checkpoints-on-close, or zero (the
|
||||
** default) to enable them, and negative to leave the setting unchanged.
|
||||
** The second parameter is a pointer to an integer
|
||||
** <dd> Usually, when a database in [WAL mode] is closed or detached from a
|
||||
** database handle, SQLite checks if if there are other connections to the
|
||||
** same database, and if there are no other database connection (if the
|
||||
** connection being closed is the last open connection to the database),
|
||||
** then SQLite performs a [checkpoint] before closing the connection and
|
||||
** deletes the WAL file. The SQLITE_DBCONFIG_NO_CKPT_ON_CLOSE option can
|
||||
** be used to override that behavior. The first argument passed to this
|
||||
** operation (the third parameter to [sqlite3_db_config()]) is an integer
|
||||
** which is positive to disable checkpoints-on-close, or zero (the default)
|
||||
** to enable them, and negative to leave the setting unchanged.
|
||||
** The second argument (the fourth parameter) is a pointer to an integer
|
||||
** into which is written 0 or 1 to indicate whether checkpoints-on-close
|
||||
** have been disabled - 0 if they are not disabled, 1 if they are.
|
||||
** </dd>
|
||||
@ -2500,7 +2551,7 @@ struct sqlite3_mem_methods {
|
||||
** statistics. For statistics to be collected, the flag must be set on
|
||||
** the database handle both when the SQL statement is prepared and when it
|
||||
** is stepped. The flag is set (collection of statistics is enabled)
|
||||
** by default. This option takes two arguments: an integer and a pointer to
|
||||
** by default. <p>This option takes two arguments: an integer and a pointer to
|
||||
** an integer.. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the statement scanstatus option. If the second argument
|
||||
** is not NULL, then the value of the statement scanstatus setting after
|
||||
@ -2514,7 +2565,7 @@ struct sqlite3_mem_methods {
|
||||
** in which tables and indexes are scanned so that the scans start at the end
|
||||
** and work toward the beginning rather than starting at the beginning and
|
||||
** working toward the end. Setting SQLITE_DBCONFIG_REVERSE_SCANORDER is the
|
||||
** same as setting [PRAGMA reverse_unordered_selects]. This option takes
|
||||
** same as setting [PRAGMA reverse_unordered_selects]. <p>This option takes
|
||||
** two arguments which are an integer and a pointer to an integer. The first
|
||||
** argument is 1, 0, or -1 to enable, disable, or leave unchanged the
|
||||
** reverse scan order flag, respectively. If the second argument is not NULL,
|
||||
@ -2523,7 +2574,76 @@ struct sqlite3_mem_methods {
|
||||
** first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE option enables or disables
|
||||
** the ability of the [ATTACH DATABASE] SQL command to create a new database
|
||||
** file if the database filed named in the ATTACH command does not already
|
||||
** exist. This ability of ATTACH to create a new database is enabled by
|
||||
** default. Applications can disable or reenable the ability for ATTACH to
|
||||
** create new database files using this DBCONFIG option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the attach-create flag, respectively. If the second
|
||||
** argument is not NULL, then 0 or 1 is written into the integer that the
|
||||
** second argument points to depending on if the attach-create flag is set
|
||||
** after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE option enables or disables the
|
||||
** ability of the [ATTACH DATABASE] SQL command to open a database for writing.
|
||||
** This capability is enabled by default. Applications can disable or
|
||||
** reenable this capability using the current DBCONFIG option. If the
|
||||
** the this capability is disabled, the [ATTACH] command will still work,
|
||||
** but the database will be opened read-only. If this option is disabled,
|
||||
** then the ability to create a new database using [ATTACH] is also disabled,
|
||||
** regardless of the value of the [SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE]
|
||||
** option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the ability to ATTACH another database for writing,
|
||||
** respectively. If the second argument is not NULL, then 0 or 1 is written
|
||||
** into the integer to which the second argument points, depending on whether
|
||||
** the ability to ATTACH a read/write database is enabled or disabled
|
||||
** after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** [[SQLITE_DBCONFIG_ENABLE_COMMENTS]]
|
||||
** <dt>SQLITE_DBCONFIG_ENABLE_COMMENTS</dt>
|
||||
** <dd>The SQLITE_DBCONFIG_ENABLE_COMMENTS option enables or disables the
|
||||
** ability to include comments in SQL text. Comments are enabled by default.
|
||||
** An application can disable or reenable comments in SQL text using this
|
||||
** DBCONFIG option.<p>
|
||||
** This option takes two arguments which are an integer and a pointer
|
||||
** to an integer. The first argument is 1, 0, or -1 to enable, disable, or
|
||||
** leave unchanged the ability to use comments in SQL text,
|
||||
** respectively. If the second argument is not NULL, then 0 or 1 is written
|
||||
** into the integer that the second argument points to depending on if
|
||||
** comments are allowed in SQL text after processing the first argument.
|
||||
** </dd>
|
||||
**
|
||||
** </dl>
|
||||
**
|
||||
** [[DBCONFIG arguments]] <h3>Arguments To SQLITE_DBCONFIG Options</h3>
|
||||
**
|
||||
** <p>Most of the SQLITE_DBCONFIG options take two arguments, so that the
|
||||
** overall call to [sqlite3_db_config()] has a total of four parameters.
|
||||
** The first argument (the third parameter to sqlite3_db_config()) is a integer.
|
||||
** The second argument is a pointer to an integer. If the first argument is 1,
|
||||
** then the option becomes enabled. If the first integer argument is 0, then the
|
||||
** option is disabled. If the first argument is -1, then the option setting
|
||||
** is unchanged. The second argument, the pointer to an integer, may be NULL.
|
||||
** If the second argument is not NULL, then a value of 0 or 1 is written into
|
||||
** the integer to which the second argument points, depending on whether the
|
||||
** setting is disabled or enabled after applying any changes specified by
|
||||
** the first argument.
|
||||
**
|
||||
** <p>While most SQLITE_DBCONFIG options use the argument format
|
||||
** described in the previous paragraph, the [SQLITE_DBCONFIG_MAINDBNAME]
|
||||
** and [SQLITE_DBCONFIG_LOOKASIDE] options are different. See the
|
||||
** documentation of those exceptional options for details.
|
||||
*/
|
||||
#define SQLITE_DBCONFIG_MAINDBNAME 1000 /* const char* */
|
||||
#define SQLITE_DBCONFIG_LOOKASIDE 1001 /* void* int int */
|
||||
@ -2545,7 +2665,10 @@ struct sqlite3_mem_methods {
|
||||
#define SQLITE_DBCONFIG_TRUSTED_SCHEMA 1017 /* int int* */
|
||||
#define SQLITE_DBCONFIG_STMT_SCANSTATUS 1018 /* int int* */
|
||||
#define SQLITE_DBCONFIG_REVERSE_SCANORDER 1019 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1019 /* Largest DBCONFIG */
|
||||
#define SQLITE_DBCONFIG_ENABLE_ATTACH_CREATE 1020 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_ATTACH_WRITE 1021 /* int int* */
|
||||
#define SQLITE_DBCONFIG_ENABLE_COMMENTS 1022 /* int int* */
|
||||
#define SQLITE_DBCONFIG_MAX 1022 /* Largest DBCONFIG */
|
||||
|
||||
/*
|
||||
** CAPI3REF: Enable Or Disable Extended Result Codes
|
||||
@ -2899,6 +3022,44 @@ SQLITE_API int sqlite3_busy_handler(sqlite3*,int(*)(void*,int),void*);
|
||||
*/
|
||||
SQLITE_API int sqlite3_busy_timeout(sqlite3*, int ms);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Set the Setlk Timeout
|
||||
** METHOD: sqlite3
|
||||
**
|
||||
** This routine is only useful in SQLITE_ENABLE_SETLK_TIMEOUT builds. If
|
||||
** the VFS supports blocking locks, it sets the timeout in ms used by
|
||||
** eligible locks taken on wal mode databases by the specified database
|
||||
** handle. In non-SQLITE_ENABLE_SETLK_TIMEOUT builds, or if the VFS does
|
||||
** not support blocking locks, this function is a no-op.
|
||||
**
|
||||
** Passing 0 to this function disables blocking locks altogether. Passing
|
||||
** -1 to this function requests that the VFS blocks for a long time -
|
||||
** indefinitely if possible. The results of passing any other negative value
|
||||
** are undefined.
|
||||
**
|
||||
** Internally, each SQLite database handle store two timeout values - the
|
||||
** busy-timeout (used for rollback mode databases, or if the VFS does not
|
||||
** support blocking locks) and the setlk-timeout (used for blocking locks
|
||||
** on wal-mode databases). The sqlite3_busy_timeout() method sets both
|
||||
** values, this function sets only the setlk-timeout value. Therefore,
|
||||
** to configure separate busy-timeout and setlk-timeout values for a single
|
||||
** database handle, call sqlite3_busy_timeout() followed by this function.
|
||||
**
|
||||
** Whenever the number of connections to a wal mode database falls from
|
||||
** 1 to 0, the last connection takes an exclusive lock on the database,
|
||||
** then checkpoints and deletes the wal file. While it is doing this, any
|
||||
** new connection that tries to read from the database fails with an
|
||||
** SQLITE_BUSY error. Or, if the SQLITE_SETLK_BLOCK_ON_CONNECT flag is
|
||||
** passed to this API, the new connection blocks until the exclusive lock
|
||||
** has been released.
|
||||
*/
|
||||
SQLITE_API int sqlite3_setlk_timeout(sqlite3*, int ms, int flags);
|
||||
|
||||
/*
|
||||
** CAPI3REF: Flags for sqlite3_setlk_timeout()
|
||||
*/
|
||||
#define SQLITE_SETLK_BLOCK_ON_CONNECT 0x01
|
||||
|
||||
/*
|
||||
** CAPI3REF: Convenience Routines For Running Queries
|
||||
** METHOD: sqlite3
|
||||
@ -5014,7 +5175,7 @@ SQLITE_API const void *sqlite3_column_decltype16(sqlite3_stmt*,int);
|
||||
** other than [SQLITE_ROW] before any subsequent invocation of
|
||||
** sqlite3_step(). Failure to reset the prepared statement using
|
||||
** [sqlite3_reset()] would result in an [SQLITE_MISUSE] return from
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1],
|
||||
** sqlite3_step(). But after [version 3.6.23.1] ([dateof:3.6.23.1]),
|
||||
** sqlite3_step() began
|
||||
** calling [sqlite3_reset()] automatically in this circumstance rather
|
||||
** than returning [SQLITE_MISUSE]. This is not considered a compatibility
|
||||
@ -6910,6 +7071,8 @@ SQLITE_API int sqlite3_autovacuum_pages(
|
||||
**
|
||||
** ^The second argument is a pointer to the function to invoke when a
|
||||
** row is updated, inserted or deleted in a rowid table.
|
||||
** ^The update hook is disabled by invoking sqlite3_update_hook()
|
||||
** with a NULL pointer as the second parameter.
|
||||
** ^The first argument to the callback is a copy of the third argument
|
||||
** to sqlite3_update_hook().
|
||||
** ^The second callback argument is one of [SQLITE_INSERT], [SQLITE_DELETE],
|
||||
@ -10748,8 +10911,9 @@ SQLITE_API SQLITE_EXPERIMENTAL int sqlite3_snapshot_recover(sqlite3 *db, const c
|
||||
/*
|
||||
** CAPI3REF: Serialize a database
|
||||
**
|
||||
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to memory
|
||||
** that is a serialization of the S database on [database connection] D.
|
||||
** The sqlite3_serialize(D,S,P,F) interface returns a pointer to
|
||||
** memory that is a serialization of the S database on
|
||||
** [database connection] D. If S is a NULL pointer, the main database is used.
|
||||
** If P is not a NULL pointer, then the size of the database in bytes
|
||||
** is written into *P.
|
||||
**
|
||||
@ -11391,9 +11555,10 @@ SQLITE_API void sqlite3session_table_filter(
|
||||
** is inserted while a session object is enabled, then later deleted while
|
||||
** the same session object is disabled, no INSERT record will appear in the
|
||||
** changeset, even though the delete took place while the session was disabled.
|
||||
** Or, if one field of a row is updated while a session is disabled, and
|
||||
** another field of the same row is updated while the session is enabled, the
|
||||
** resulting changeset will contain an UPDATE change that updates both fields.
|
||||
** Or, if one field of a row is updated while a session is enabled, and
|
||||
** then another field of the same row is updated while the session is disabled,
|
||||
** the resulting changeset will contain an UPDATE change that updates both
|
||||
** fields.
|
||||
*/
|
||||
SQLITE_API int sqlite3session_changeset(
|
||||
sqlite3_session *pSession, /* Session object */
|
||||
@ -11465,8 +11630,9 @@ SQLITE_API sqlite3_int64 sqlite3session_changeset_size(sqlite3_session *pSession
|
||||
** database zFrom the contents of the two compatible tables would be
|
||||
** identical.
|
||||
**
|
||||
** It an error if database zFrom does not exist or does not contain the
|
||||
** required compatible table.
|
||||
** Unless the call to this function is a no-op as described above, it is an
|
||||
** error if database zFrom does not exist or does not contain the required
|
||||
** compatible table.
|
||||
**
|
||||
** If the operation is successful, SQLITE_OK is returned. Otherwise, an SQLite
|
||||
** error code. In this case, if argument pzErrMsg is not NULL, *pzErrMsg
|
||||
@ -11601,7 +11767,7 @@ SQLITE_API int sqlite3changeset_start_v2(
|
||||
** The following flags may passed via the 4th parameter to
|
||||
** [sqlite3changeset_start_v2] and [sqlite3changeset_start_v2_strm]:
|
||||
**
|
||||
** <dt>SQLITE_CHANGESETAPPLY_INVERT <dd>
|
||||
** <dt>SQLITE_CHANGESETSTART_INVERT <dd>
|
||||
** Invert the changeset while iterating through it. This is equivalent to
|
||||
** inverting a changeset using sqlite3changeset_invert() before applying it.
|
||||
** It is an error to specify this flag with a patchset.
|
||||
@ -11916,19 +12082,6 @@ SQLITE_API int sqlite3changeset_concat(
|
||||
void **ppOut /* OUT: Buffer containing output changeset */
|
||||
);
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Upgrade the Schema of a Changeset/Patchset
|
||||
*/
|
||||
SQLITE_API int sqlite3changeset_upgrade(
|
||||
sqlite3 *db,
|
||||
const char *zDb,
|
||||
int nIn, const void *pIn, /* Input changeset */
|
||||
int *pnOut, void **ppOut /* OUT: Inverse of input */
|
||||
);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
** CAPI3REF: Changegroup Handle
|
||||
**
|
||||
|
4
deps/sqlite/sqlite3ext.h
vendored
@ -366,6 +366,8 @@ struct sqlite3_api_routines {
|
||||
/* Version 3.44.0 and later */
|
||||
void *(*get_clientdata)(sqlite3*,const char*);
|
||||
int (*set_clientdata)(sqlite3*, const char*, void*, void(*)(void*));
|
||||
/* Version 3.50.0 and later */
|
||||
int (*setlk_timeout)(sqlite3*,int,int);
|
||||
};
|
||||
|
||||
/*
|
||||
@ -699,6 +701,8 @@ typedef int (*sqlite3_loadext_entry)(
|
||||
/* Version 3.44.0 and later */
|
||||
#define sqlite3_get_clientdata sqlite3_api->get_clientdata
|
||||
#define sqlite3_set_clientdata sqlite3_api->set_clientdata
|
||||
/* Version 3.50.0 and later */
|
||||
#define sqlite3_setlk_timeout sqlite3_api->setlk_timeout
|
||||
#endif /* !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION) */
|
||||
|
||||
#if !defined(SQLITE_CORE) && !defined(SQLITE_OMIT_LOAD_EXTENSION)
|
||||
|
1
deps/zsign
vendored
Submodule
1
docs
63
docs/app_development_cheat_sheet.md
Normal file
@ -0,0 +1,63 @@
|
||||
# App Development Cheat Sheet
|
||||
|
||||
Making apps for the impatient tilde friend.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- either run your own instance or use [tildefriends.net](https://www.tildefriends.net/)
|
||||
- register and login
|
||||
- [optional] use the `ssb` app to create yourself an SSB identity
|
||||
|
||||
## Development Process
|
||||
|
||||
1. hit the `edit` link from any app or new app URL
|
||||
2. make sure the path in the text box is under your username: `/~username/app/`
|
||||
3. write server-side code in `app.js`
|
||||
4. click the `save` button or press the save hotkey (Alt+S or _[browser-specific modifiers]_+S)
|
||||
5. see the app reload on the right side
|
||||
|
||||
## Output
|
||||
|
||||
- `app.setDocument(html)` - send HTML to the browser
|
||||
- `print(...)` - send values to the browser's developer console
|
||||
|
||||
## Persistence
|
||||
|
||||
- `app.localStorageGet(key)` -> `value`
|
||||
- `app.localStorageSet(key, value)`
|
||||
- `database()`, `shared_database(key)`, `my_shared_database(package, key)`
|
||||
- `db.get(key)` -> `value`
|
||||
- `db.set(key, value)`
|
||||
- `db.exchange(key, expected, value)` -> `exchanged`
|
||||
- `db.remove(key)`
|
||||
- `db.getAll()` -> `[key1, ...]`
|
||||
- `db.getLike(pattern)` -> `{key1: value1, ...}`
|
||||
|
||||
## SSB
|
||||
|
||||
- `ssb.createIdentity()` -> `id`
|
||||
- `ssb.getIdentities()` -> `[id1, ...]`
|
||||
- `ssb.appendMessageWithIdentity(id, content)` -> `message_id`
|
||||
- `ssb.blobStore(blob)` -> `blob_id`
|
||||
- `ssb.blobGet(id)` -> `blob`
|
||||
- `ssb.sqlAsync(query, args, row_callback)`
|
||||
|
||||
## TF-RPC
|
||||
|
||||
Stock helper code for calling functions across the web server and browser boundary.
|
||||
|
||||
- on the server: `import * as tfrpc from "/tfrpc.js";`
|
||||
- in the browser: `import * as tfrpc from "/static/tfrpc.js";`
|
||||
- either direction:
|
||||
- register a function: `tfrpc.register(function my_function() {});`
|
||||
- call a remote function: `let promise = tfrpc.rpc.my_function();`
|
||||
|
||||
## Share
|
||||
|
||||
- give out web links: [https://www.tildefriends.net/~cory/screwble/](https://www.tildefriends.net/~cory/screwble/)
|
||||
- use the `Attach App` button when composing a post in [the SSB app](https://www.tildefriends.net/~core/ssb/)
|
||||
|
||||
## More Docs
|
||||
|
||||
- [api reference](https://www.tildefriends.net/~cory/api/)
|
||||
- [source code](https://dev.tildefriends.net/cory/tildefriends/releases)
|
166
docs/app_development_guide.md
Normal file
@ -0,0 +1,166 @@
|
||||
# App Development Guide
|
||||
|
||||
A Tilde Friends application starts with code that runs on a Tilde Friends server, possibly far away from where you wrote it, in a little JavaScript environment, in its own restricted process, with the only access to the outside world being the ability to send messages to the server. This document gives some recipes showing how that can be used to build a functional user-facing application in light of the unique constraints present.
|
||||
|
||||
## Example 1: Hello, world!
|
||||
|
||||
Of course we must start with a classic.
|
||||
|
||||
### app.js
|
||||
|
||||
```js
|
||||
app.setDocument('<h1 style="color: #fff">Hello, world!</h1>');
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
<h1 style="color: #fff">Hello, world!</h1>
|
||||
|
||||
### Explanation
|
||||
|
||||
At a glance, this might seem mundane, but for it to work:
|
||||
|
||||
- the server starts a real process for your app and loads your code into it
|
||||
- your code runs
|
||||
- `app.setDocument()` sends a message back to the server
|
||||
- the server interprets the message and redirects it to the browser
|
||||
- `core/client.js` in the browser receives the message and puts your HTML into an iframe
|
||||
- your HTML is presented by the browser in an iframe sandbox
|
||||
|
||||
But you don't have to think about all that. Call a function, and you see the result.
|
||||
|
||||
## Example 2: Hit Counter
|
||||
|
||||
Let's take advantage of code running on the server and create a little hit counter using a key value store shared between all visitors.
|
||||
|
||||
### app.js
|
||||
|
||||
```js
|
||||
async function main() {
|
||||
let db = await shared_database('visitors');
|
||||
let count = parseInt((await db.get('visitors')) ?? '0') + 1;
|
||||
await db.set('visitors', count.toString());
|
||||
await app.setDocument(`
|
||||
<h1 style="color: #fff">Welcome, visitor #${count}!</h1>
|
||||
`);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
<h1 style="color: #fff">Welcome, visitor #1!</h1>
|
||||
|
||||
### Explanation
|
||||
|
||||
Just as pure browser apps have access to `localStorage`, Tilde Friends apps have access to key-value storage on the server.
|
||||
|
||||
The interface is a bit clunky and will likely change someday, but this example gets a database object, from which you can get and set string values by key. There are various on `shared_database` that let you store data that is private to the user or shared by different criteria.
|
||||
|
||||
Also, even though any browser-side code is sandboxed, it is allowed to access browser local storage by going through Tilde Friends API, because sometimes that is useful.
|
||||
|
||||
## Example 3: Files
|
||||
|
||||
Suppose you don't want to create your entire app in a single server-side file as we've done with the previous examples. There are some tools to allow you to begin to organize.
|
||||
|
||||
### app.js
|
||||
|
||||
```js
|
||||
async function main() {
|
||||
let html = utf8Decode(await getFile('index.html'));
|
||||
app.setDocument(html);
|
||||
}
|
||||
|
||||
main();
|
||||
```
|
||||
|
||||
### index.html
|
||||
|
||||
```html
|
||||
<html>
|
||||
<head>
|
||||
<script type="module" src="script.js"></script>
|
||||
</head>
|
||||
<body style="color: #fff">
|
||||
<h1>File Test</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
### script.js
|
||||
|
||||
```js
|
||||
window.addEventListener('load', function() {
|
||||
document.body.appendChild(document.createTextNode('Hello, world');
|
||||
});
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
<h1>File Test</h1><p>Hello, world!</p>
|
||||
|
||||
### Explanation
|
||||
|
||||
On the server, `utf8Decode(await getFile(fileName))` lets you load a file from your app. In the browser, your app files are made available by HTTP, so you can `<script src="my_script.js"></script>` and such to access them.
|
||||
|
||||
## Example 4: Remote Procedure Call
|
||||
|
||||
While making calls between the client and the server, it is possible to pass functions across that boundary. `tfrpc.js` is a tiny script which builds on that feature to try to hide some of the complexities.
|
||||
|
||||
### app.js
|
||||
|
||||
```js
|
||||
import * as tf from '/tfrpc.js';
|
||||
|
||||
function sum() {
|
||||
let s = 0;
|
||||
for (let x of arguments) {
|
||||
s += x;
|
||||
}
|
||||
return s;
|
||||
}
|
||||
tf.register(sum);
|
||||
|
||||
async function main() {
|
||||
app.setDocument(utf8Decode(await getFile('index.html')));
|
||||
}
|
||||
main();
|
||||
```
|
||||
|
||||
### index.html
|
||||
|
||||
```html
|
||||
<html>
|
||||
<body>
|
||||
<h1 id="result">Calculating...</h1>
|
||||
</body>
|
||||
<script type="module" src="script.js"></script>
|
||||
</html>
|
||||
```
|
||||
|
||||
### script.js
|
||||
|
||||
```js
|
||||
import * as tf from '/static/tfrpc.js';
|
||||
|
||||
window.addEventListener('load', async function () {
|
||||
document.getElementById('result').innerText = await tf.rpc.sum(1, 2, 3);
|
||||
});
|
||||
```
|
||||
|
||||
### Output
|
||||
|
||||
<h1>6</h1>
|
||||
|
||||
### Explanation
|
||||
|
||||
Here the browser makes an asynchronous call to the server to do some basic math and update its DOM with the result.
|
||||
|
||||
With your favorite Vue/Lit/React/... library on the client-side and your favorite Tilde Friends API calls registered with tfrpc, it becomes pretty easy to start extracting interesting information from, say, SQL queries over Secure Scuttlebutt data, and generating complicated, dynamic user interface. These are the building blocks I used to make the current Tilde Friends SSB client interface.
|
||||
|
||||
## Conclusion
|
||||
|
||||
Tilde Friends is currently a pile of all the parts that I thought I needed to build interesting web applications, tied together by code that tries to walk the fine line between being secure enough to let us safely run code on the same device and being usable enough that you can open a tab in your browser and start building just by typing code.
|
||||
|
||||
I don't claim it thoroughly accomplishes either yet, but I believe it is at a stage where it is showing how promising this approach can be, and I am excited for you to take it for a spin and share.
|
40
docs/connecting_manyverse.md
Normal file
@ -0,0 +1,40 @@
|
||||
# Connecting with Manyverse
|
||||
|
||||
Communication with [Manyverse](https://www.manyver.se/) should Just Work (tm).
|
||||
|
||||
This document is intended as a cheat sheet for the instances where it doesn't.
|
||||
If your experience differs, please share so we can make things better.
|
||||
|
||||
## Connecting Manyverse to the tildefriends.net room
|
||||
|
||||
Open the `Connections` tab. This is from the desktop app, but mobile is similar.
|
||||
|
||||

|
||||
|
||||
Open the `Connections Panel`.
|
||||
|
||||

|
||||
|
||||
Use the `Add Connection` button at the bottom right to open the dialog to enter
|
||||
a connections string to add a new connection.
|
||||
|
||||

|
||||
|
||||
Copy the tildefriends.net room code from https://www.tildefriends.net/~cory/room/.
|
||||
|
||||

|
||||
|
||||
Paste.
|
||||
|
||||
On mobile especially, make sure the full text is pasted without modification.
|
||||
|
||||

|
||||
|
||||
Click `Done`, and you should be connected successfully. tildefriends.net is
|
||||
all things: a room, a pub, and a client, so you should be able to start replicating
|
||||
immediately as well as find other similarly connected people with whom to establish
|
||||
further connections.
|
||||
|
||||
When logged into tildefriends.net, active connections it sees can be found on
|
||||
the `Connections` tab: https://www.tildefriends.net/~core/ssb/#connections,
|
||||
which may indicate errors if you find yourself disconnecting.
|
BIN
docs/images/manyverse_code.png
Normal file
After Width: | Height: | Size: 43 KiB |
BIN
docs/images/manyverse_connections_panel.png
Normal file
After Width: | Height: | Size: 27 KiB |
BIN
docs/images/manyverse_connections_tab.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
docs/images/manyverse_paste_invite_code.png
Normal file
After Width: | Height: | Size: 25 KiB |
BIN
docs/images/tildefriends_room_app.png
Normal file
After Width: | Height: | Size: 136 KiB |
15
docs/inspiration.md
Normal file
@ -0,0 +1,15 @@
|
||||
# Inspiration
|
||||
|
||||
This is an ever-growing list of software that is similar to what Tilde Friends tries to be but as far as I can tell don't quite fit the same niche.
|
||||
|
||||
- Secure Scuttlebutt Clients
|
||||
- [Manyverse](https://www.manyver.se/)
|
||||
- [Patchwork](https://github.com/ssbc/patchwork)
|
||||
- [Patchfox](https://patchfox.org/#/)
|
||||
- [Habitat](https://gitlab.com/quickdudley/habitat)
|
||||
- [Āhau](https://gitlab.com/ahau/ahau/)
|
||||
- [erlbutt](https://github.com/cmoid/erlbutt/)
|
||||
- Web Application Platforms
|
||||
- [Glitch](https://glitch.com/)
|
||||
- [Val Town](https://www.val.town/)
|
||||
- [Clace](https://clace.io/)
|
24
docs/release_checklist.md
Normal file
@ -0,0 +1,24 @@
|
||||
# Release Checklist
|
||||
|
||||
- make sure ci is passing
|
||||
- run the tests
|
||||
- format + prettier
|
||||
- update metadata/en-US/changelogs
|
||||
- git tag v1.2.3
|
||||
- git tag -f latest_release
|
||||
- push
|
||||
- make a release on gitea
|
||||
- upload the artifacts
|
||||
- upload the AppImage and zsyncmake
|
||||
- upload to Google
|
||||
- upload to Apple with dist-ios on macos
|
||||
- nix
|
||||
- june and december: update release version
|
||||
- run `nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update`
|
||||
- comment out the hash in default.nix
|
||||
- update the version
|
||||
- run `nix-build`
|
||||
- update the hash
|
||||
- bump the versions in GNUmakefile for the next release
|
||||
- make
|
||||
- commit
|
18
docs/upgrading.md
Normal file
@ -0,0 +1,18 @@
|
||||
# Upgrading
|
||||
|
||||
Tilde Friends can be upgraded simply by running a new executable against an
|
||||
existing database.
|
||||
|
||||
Tilde Friends writes all data to a `db.sqlite` file, either in
|
||||
`~/.local/share/tildefriends/` or in the working directory where it is run,
|
||||
depending on the platform and whether each one already exists. Run with
|
||||
`tildefriends run -d DB_PATH` to specify the path to the database explicitly.
|
||||
|
||||
This file can be copied and moved across machines as needed like any [sqlite3
|
||||
database](https://www.sqlite.org/onefile.html).
|
||||
|
||||
Schema changes and compatibility breaks have been rare, by design. In general,
|
||||
upgrading is not expected to require any manual intervention and likely does
|
||||
not involve any automatic migration, either. Downgrading is not well-supported
|
||||
but will probably just work excepting rare changes that will be called out in
|
||||
the changelog.
|
288
docs/usage.md
Normal file
@ -0,0 +1,288 @@
|
||||
# CLI Usage
|
||||
|
||||
## tildefriends -h
|
||||
|
||||
```
|
||||
Usage: out/debug/tildefriends command [command-options]
|
||||
commands:
|
||||
run - Run tildefriends (default).
|
||||
sandbox - Run a sandboxed tildefriends sandbox process (used internally).
|
||||
import - Import apps from file to the database.
|
||||
export - Export apps from the database to file.
|
||||
publish - Append a message to a feed.
|
||||
private - Append a private post message to a feed.
|
||||
create_invite - Create an invite.
|
||||
get_sequence - Get the last sequence number for a feed.
|
||||
get_identity - Get the server account identity.
|
||||
get_profile - Get profile information for the given identity.
|
||||
get_contacts - Get information about followed, blocked, and friend identities.
|
||||
has_blob - Check whether a blob is in the blob store.
|
||||
get_blob - Read a file from the blob store.
|
||||
store_blob - Write a file to the blob store.
|
||||
verify - Verify a feed.
|
||||
test - Test SSB.
|
||||
```
|
||||
|
||||
## tildefriends run -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends run [options]
|
||||
|
||||
Run tildefriends (default).
|
||||
|
||||
options:
|
||||
-s, --script script Script to run (default: core/core.js).
|
||||
-d, --db-path path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-k, --ssb-network-key key SSB network key to use.
|
||||
-n, --count count Number of instances to run.
|
||||
-a, --args args Arguments of the format key=value,foo=bar,verbose=true (note: these are persisted to the database).
|
||||
code_of_conduct (default: ""): Code of conduct presented at sign-in.
|
||||
ssb_port (default: 8008): Port on which to listen for SSB secure handshake connections.
|
||||
http_local_only (default: false): Whether to bind http(s) to the loopback address. Otherwise any.
|
||||
http_port (default: 12345): Port on which to listen for HTTP connections.
|
||||
https_port (default: 0): Port on which to listen for secure HTTP connections.
|
||||
out_http_port_file (default: ""): File to which to write bound HTTP port.
|
||||
blob_fetch_age_seconds (default: -1): Only blobs mentioned more recently than this age will be automatically fetched.
|
||||
blob_expire_age_seconds (default: -1): Blobs older than this will be automatically deleted.
|
||||
fetch_hosts (default: ""): Comma-separated list of host names to which HTTP fetch requests are allowed. None if empty.
|
||||
http_redirect (default: ""): If connecting by HTTP and HTTPS is configured, Location header prefix (ie, "http://example.com")
|
||||
index (default: "/~core/intro/"): Default path.
|
||||
index_map (default: ""): Mappings from hostname to redirect path, one per line, as in: "www.tildefriends.net=/~core/index/"
|
||||
peer_exchange (default: false): Enable discovery of, sharing of, and connecting to internet peer strangers, including announcing this instance.
|
||||
replicator (default: true): Enable message and blob replication.
|
||||
room (default: true): Enable peers to tunnel through this instance as a room.
|
||||
room_name (default: "tilde friends tunnel"): Name of the room.
|
||||
seeds_host (default: "seeds.tildefriends.net"): Hostname for seed connections.
|
||||
account_registration (default: true): Allow registration of new accounts.
|
||||
replication_hops (default: 2): Number of hops to replicate (1 = direct follows, 2 = follows of follows, etc.).
|
||||
delete_stale_feeds (default: false): Periodically delete feeds that aren't visible from local accounts or related follows.
|
||||
talk_to_strangers (default: true): Whether connections are accepted from accounts that aren't in the replication range or otherwise already known.
|
||||
autologin (default: false): Whether mobile autologin is supported.
|
||||
broadcast (default: true): Send network discovery broadcasts.
|
||||
discovery (default: true): Receive network discovery broadcasts.
|
||||
-o, --one-proc Run everything in one process (unsafely!).
|
||||
-z, --zip path Zip archive from which to load files.
|
||||
-v, --verbose Log raw messages.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends sandbox -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends sandbox [options]
|
||||
|
||||
Run a sandboxed tildefriends sandbox process (used internally).
|
||||
|
||||
options:
|
||||
-h, --help Show this usage information.
|
||||
-f, --fd File descriptor with which to communicate with parent process.
|
||||
```
|
||||
|
||||
## tildefriends import -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends import [options] [paths...]
|
||||
|
||||
Import apps from file to the database.
|
||||
|
||||
options:
|
||||
-u, --user user User into whose account apps will be imported (default: "import").
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends export -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends export [options] [paths...]
|
||||
|
||||
Export apps from the database to file.
|
||||
|
||||
options:
|
||||
-u, --user user User from whose account apps will be exported (default: "core").
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
|
||||
paths Paths of apps to export (example: /~core/ssb /~user/app).
|
||||
```
|
||||
|
||||
## tildefriends publish -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends publish [options]
|
||||
|
||||
Append a message to a feed.
|
||||
|
||||
options:
|
||||
-u, --user user User owning identity with which to publish.
|
||||
-i, --id identity Identity with which to publish message.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-c, --content json JSON content of message to publish.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends private -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends private [options]
|
||||
|
||||
Append a private post message to a feed.
|
||||
|
||||
options:
|
||||
-u, --user user User owning identity with which to publish (optional).
|
||||
-i, --id identity Identity with which to publish message.
|
||||
-r, --recipients recipients Recipient identities.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-t, --text text Private post text.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends create_invite -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends create_invite [options]
|
||||
|
||||
Create an invite.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get latest sequence number.
|
||||
-a, --address address Address to which the recipient will connect.
|
||||
-p, --port port Port to which the recipient will connect.
|
||||
-u, --use_count count Number of times this invite may be used (default: 1).
|
||||
-e, --expires seconds How long this invite is valid in seconds (-1 for indefinitely, default: 1 hour).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_sequence -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_sequence [options]
|
||||
|
||||
Get the last sequence number for a feed.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get latest sequence number.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_identity -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_identity [options]
|
||||
|
||||
Get the server account identity.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_profile -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_profile [options]
|
||||
|
||||
Get profile information for the given identity.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account for which to get profile information.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_contacts -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_contacts [options]
|
||||
|
||||
Get information about followed, blocked, and friend identities.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-i, --identity identity Account from which to get contact information.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends has_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends has_blob [options]
|
||||
|
||||
Check whether a blob is in the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-b, --blob_id blob_id ID of blob to query.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends get_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends get_blob [options]
|
||||
|
||||
Read a file from the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-b, --blob blob_id Blob identifier to retrieve.
|
||||
-o, --output file_path Location to write the retrieved blob.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends store_blob -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends store_blob [options]
|
||||
|
||||
Write a file to the blob store.
|
||||
|
||||
options:
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-f, --file file_path Path to file to add to the blob store.
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends verify -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends verify [options]
|
||||
|
||||
Verify a feed.
|
||||
|
||||
options:
|
||||
-i, --identity identity Identity to verify.
|
||||
-s, --sequence sequence Sequence number to debug.
|
||||
-d, --db-path db_path SQLite database path (default: /home/cory/.local/share/tildefriends/db.sqlite).
|
||||
-h, --help Show this usage information.
|
||||
```
|
||||
|
||||
## tildefriends test -h
|
||||
|
||||
```
|
||||
|
||||
Usage: out/debug/tildefriends test [options]
|
||||
|
||||
Test SSB.
|
||||
|
||||
options:
|
||||
-t, --tests tests Comma-separated list of tests to run. (default: all)
|
||||
-h, --help Show this usage information.
|
||||
```
|
64
docs/vision.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Vision
|
||||
|
||||
Tilde Friends is a tool for making and sharing.
|
||||
|
||||
It is both a peer-to-peer social network client, participating in Secure
|
||||
Scuttlebutt, and an environment for creating and running web applications.
|
||||
|
||||
## Why
|
||||
|
||||
This is a thing that I wanted to exist and wanted to work on. No other reason.
|
||||
There is not a business model. I believe it is interesting and unique.
|
||||
|
||||
## Goals
|
||||
|
||||
1. Make it **easy and fun** to run all sorts of web applications.
|
||||
|
||||
2. Provide **security** that is easy to understand and protects your data.
|
||||
|
||||
3. Make **creating and sharing** web applications accessible to anyone with a
|
||||
browser.
|
||||
|
||||
## Ways to Use Tilde Friends
|
||||
|
||||
1. **Social Network User**: This is a social network first. You are just here,
|
||||
because your friends are. Or you like how we limit your message length or
|
||||
short videos or whatever the trend is. If you are ambitious, you click links
|
||||
and see interactive experiences (apps) that you wouldn't see elsewhere.
|
||||
|
||||
2. **Web Visitor**: You get links from a friend to meeting invites, polls, games,
|
||||
lists, wiki pages, ..., and you interact with them as though they were
|
||||
cloud-hosted by a megacorporation. They just work, and you don't think twice.
|
||||
|
||||
3. **Group leader**: You host or use a small public instance, installing apps for
|
||||
a group of friends to use as web visitors.
|
||||
|
||||
4. **Developer**: You like to write code and make or improve apps for fun or to
|
||||
solve problems. When you encounter a Tilde Friends app on a strange server,
|
||||
you know you can trivially modify it or download it to your own instance.
|
||||
|
||||
## Future Goals / Endgame
|
||||
|
||||
1. Mobile apps. This can run on your old phone. Maybe you won't be hosting
|
||||
the web interface publicly, but you can sync, install and edit apps, and
|
||||
otherwise get the full experience from a tiny touch screen.
|
||||
|
||||
2. The universal application runtime. The web browser is the universal
|
||||
platform, but even for the simplest application that you might want to host
|
||||
for your friends, cloud hosting, containers, and complicated dependencies might
|
||||
all enter the mix. Tilde Friends, though it is yet another thing to host,
|
||||
includes everything you need out of the box to run a vast variety of interesting
|
||||
apps.
|
||||
|
||||
Tilde Friends will be built out, gradually providing safe access to host
|
||||
resources and client resources the same way web browsers extended access to
|
||||
resources like GPU, persistent storage, cameras, ... over the years.
|
||||
|
||||
Not much effort has been put forward yet to having a robust, long-lasting API,
|
||||
but since the client side longevity is already handled by web browsers, it
|
||||
seems possible that the server-side API can be managed in a similar way.
|
||||
|
||||
3. An awesome development environment. Right now it runs JavaScript from the
|
||||
first embeddable text editor I could poorly configure enough to edit code,
|
||||
but it could incorporate a debugger, source control integration a la ssb-git,
|
||||
merge tools, and transpiling from all sorts of different languages.
|
14
flake.lock
generated
@ -5,11 +5,11 @@
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@ -20,16 +20,16 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1717281328,
|
||||
"narHash": "sha256-evZPzpf59oNcDUXxh2GHcxHkTEG4fjae2ytWP85jXRo=",
|
||||
"lastModified": 1748037224,
|
||||
"narHash": "sha256-92vihpZr6dwEMV6g98M5kHZIttrWahb9iRPBm1atcPk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "b3b2b28c1daa04fe2ae47c21bb76fd226eac4ca1",
|
||||
"rev": "f09dede81861f3a83f7f06641ead34f02f37597f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"ref": "nixos-24.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
|
@ -2,7 +2,7 @@
|
||||
description = "Tilde Friends is a platform for making, running, and sharing web applications.";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
|
15
metadata/en-US/changelogs/33.txt
Normal file
@ -0,0 +1,15 @@
|
||||
* Allow specifying all global settings from the command-line (CLI usage changed).
|
||||
* Replication improvements.
|
||||
* An iOS build is on TestFlight.
|
||||
* macOS targets are debug and release like everywhere else.
|
||||
* Running from a subdirectory is fine.
|
||||
* Patchwork compatibility improvements.
|
||||
* Invite fixes.
|
||||
* Follow/block UI fixes.
|
||||
* Mobile automatically logs in.
|
||||
* Updates:
|
||||
* CodeMirror
|
||||
* OpenSSL 3.4.1
|
||||
* libbacktrace
|
||||
* speedscope 1.22.2
|
||||
* sqlite 3.49.1
|
11
metadata/en-US/changelogs/34.txt
Normal file
@ -0,0 +1,11 @@
|
||||
* The connections tab now shows replication progress.
|
||||
* Replication performance and thoroughness improvements.
|
||||
* Bind only to localhost on mobile, configurable.
|
||||
* Request blobs referenced by referenced blobs, and improve performance of that
|
||||
query.
|
||||
* Fix file upload on iOS.
|
||||
* Add rough back/forward/refresh buttons on iOS.
|
||||
* Other crash fixes and performance improvements.
|
||||
* Updates:
|
||||
* CodeMirror
|
||||
* w3.css
|
12
metadata/en-US/changelogs/35.txt
Normal file
@ -0,0 +1,12 @@
|
||||
* Faster loads.
|
||||
* Replication fixes.
|
||||
* Shutdown fixes.
|
||||
* Consolidated message actions into a % menu.
|
||||
* Fixed and tested handling of user permissions.
|
||||
* Add a very work in progress "web" app.
|
||||
* Updates:
|
||||
* CodeMirror
|
||||
* Lit 3.3.0
|
||||
* OpenSSL 3.5.0
|
||||
* c-ares 1.34.5
|
||||
* libbacktrace
|
13
metadata/en-US/changelogs/37.txt
Normal file
@ -0,0 +1,13 @@
|
||||
* Faster loads.
|
||||
* Minor UI tweaks.
|
||||
* Added an intro app as part of the initial flow for first-time users.
|
||||
* Fixed more shutdown issues.
|
||||
* Fixed a longstanding potential database issue.
|
||||
* Added a blob export command.
|
||||
* Refresh blob wants for blobs that are requested over the web.
|
||||
* Updates:
|
||||
* CodeMirror
|
||||
* QuickJS 2025-04-26
|
||||
* libuv 1.51.0
|
||||
* sqlite 3.49.2
|
||||
* w3.css 5.02
|