Compare commits

..

628 Commits
main ... main

Author SHA1 Message Date
4855543961 docs: Not meant to be versioned. 2025-09-03 20:33:06 -04:00
cb3d6a98b9 docs: Appease all/both of the doxygen versions. 2025-09-03 18:47:34 -04:00
ada67a13d3 update: CodeMirror. 2025-09-03 12:14:58 -04:00
f4c928f26e android: Of course you can't put null in a LinkedBlockingQueue. Shrug. Fixes a shutdown crash. 2025-09-02 20:57:05 -04:00
91fd515d39 android: Cleaner shutdown still. 2025-08-27 20:23:02 -04:00
be6e841d3d android: This order seems more sensible. 2025-08-27 20:12:20 -04:00
af6afa6903 android: Don't log from the main thread. It might block? 2025-08-27 19:20:02 -04:00
6ab5d2a28d build: Start work on 0.2025.9. 2025-08-27 18:55:24 -04:00
4be033f288 build: Do the nix dance. 2025-08-27 18:54:41 -04:00
c550f92003 docs: Fix changelog version for f-droid. 2025-08-27 18:22:23 -04:00
ed836b3ee0 build: Bump this, too. 2025-08-27 18:01:57 -04:00
ac7a43abf4 build: Just kidding. This is the real 0.2025.8. 2025-08-27 18:00:48 -04:00
49f19fce91 build: Let's build 0.2025.8. 2025-08-27 12:13:16 -04:00
b2197eb8e9 docs: Update the changelog. 2025-08-27 12:13:16 -04:00
5fbc2cae1c ssb: Don't indent blockquotes so much. 2025-08-24 20:35:48 -04:00
730abb49ce android: Keep the splash screen up until we're connected to our server and loaded the page. Smooths out the launch. 2025-08-22 19:24:03 -04:00
edccab054a ssb: Make the close chat button work even when a chat isn't preexisting. 2025-08-20 20:40:02 -04:00
e8210c6fdd core: Never-ending quest to fix clean shutdown. 2025-08-20 20:20:21 -04:00
55d69d7c13 test: This seems to make -t=auto more reliable. 2025-08-20 20:19:45 -04:00
50fb18d4ff core: Remove the want: log noise. 2025-08-20 19:53:18 -04:00
77b1ea1fc8 ssb: Don't show messages that were slow to load from another channel. 2025-08-20 19:38:52 -04:00
61501a9b64 ssb: Fix closing self-chat. 2025-08-20 19:28:23 -04:00
c1507adac5 docs: Start the next changelog. 2025-08-20 19:25:44 -04:00
45fb9eda1c ssb: Add a button on profiles to open a private chat. 2025-08-20 19:19:34 -04:00
982a61f4bf ssb: Remove some debug. 2025-08-20 19:15:20 -04:00
18e5b41663 ssb: Add a button to close a private chat, removing it from the sidebar. 2025-08-20 19:08:07 -04:00
910c39cbd0 update: CodeMirror. 2025-08-20 18:07:41 -04:00
9952dfd49d ssb: Fix @-completion. 2025-08-19 12:54:19 -04:00
00f75d5382 ssb: Unread status for private messages. 2025-08-14 12:40:58 -04:00
d78828554b ssb: Fix composing private drafts. 2025-08-13 20:28:03 -04:00
b84b561109 prettier. 2025-08-13 20:14:47 -04:00
a618815500 ssb: Fix issues with private messages to one's self. 2025-08-13 20:12:27 -04:00
e1f3dc6ae4 ssb: Fix an issue with loading directly into private messages. 2025-08-13 19:58:02 -04:00
f378db6c6f ssb: Better handling of private message drafts. 2025-08-13 19:53:28 -04:00
7cec0f7d61 ssb: Fix private conversation keyboard alt+navigation. #125 2025-08-13 19:24:25 -04:00
f902d0374c ssb: Start to break out private messages by conversation. #125 2025-08-13 19:16:34 -04:00
b5f0a0c4f7 ssb: Add support for registering for blob added notifications similarly to messages. I want to use this to load images on the fly. 2025-08-13 18:26:42 -04:00
00623cea09 android: Recompile your app with 16 KB native library alignment. 2025-08-13 18:02:20 -04:00
ed4f1d6f2c android: Be smarter about the file watcher. 2025-08-13 17:51:04 -04:00
73f4a3407f ssb: Allow showing raw messages for contact messages. 2025-08-13 12:14:31 -04:00
6f11318e84 update: speedscope 1.23.1. 2025-08-13 12:06:47 -04:00
e88ee91f0e ssb: More reliably load private messages. 2025-08-06 12:10:51 -04:00
3f8daf257c update: OpenSSL 3.5.2. 2025-08-05 12:16:03 -04:00
dc387acadc ssb: Make progress bar brighter. 2025-08-02 12:16:07 -04:00
68aa41ab96 android: Tweaking random flags until ANRs subside. 2025-08-02 12:09:08 -04:00
85b23437b3 docs: Fix all the TODOCS. #39 2025-08-02 09:07:45 -04:00
c59fba817d ssb: Show the progress indicator more consistently. 2025-07-31 12:48:45 -04:00
c3415ab75c docs: Expose the rest of core to docs. 2025-07-30 20:25:20 -04:00
f1d0151d71 ssb: Make the progress bar more indefinite-looking. 2025-07-30 20:04:34 -04:00
3c5c1756d1 ssb: A progress bar experiment. 2025-07-30 19:49:08 -04:00
6a6b65d1b3 build: Update nix config. Start building 0.2025.8, switching to calver. 2025-07-30 19:26:26 -04:00
81bd54dbe6 build: Let's build 0.0.33. 2025-07-30 18:21:17 -04:00
6a1bb0d3bc update: sqlite 3.50.4. 2025-07-30 17:47:06 -04:00
705e8b553f docs: Expose the rest of the core js to doxygen. 2025-07-27 21:48:18 -04:00
e4729b22f2 docs: Hook up doxygen to some of the core JS. Now maybe I am slightly incentivized to make progress on #39. 2025-07-27 15:04:17 -04:00
662112551a core: Remove/clean up some unhelpful logging. #124 2025-07-27 14:06:46 -04:00
38fe88aab8 android: Guard aganst ANRs harder? 2025-07-27 13:17:49 -04:00
578c51faa0 update: npm. 2025-07-27 12:42:52 -04:00
a3ccc73b81 core: Move code.apps() to C. 2025-07-18 11:53:31 -04:00
7312f4d43a httpd: Oops. 2025-07-18 11:53:31 -04:00
8b546c7e02 welcome: Add a gitea icon. 2025-07-18 10:10:16 -04:00
c0b6ff2e64 update: sqlite 3.50.3. 2025-07-17 20:57:10 -04:00
638b7cc1e5 ssb: Slight suggested follows cleanup. 2025-07-16 21:01:24 -04:00
05e54e1be0 httpd: More minor cleanup. 2025-07-16 20:44:42 -04:00
4c3299ead0 core: Begin to split some of the largest modules into smaller pieces, starting with HTTP endpoints. 2025-07-16 20:12:27 -04:00
1ef56b35ad update: CodeMirror. 2025-07-16 18:59:10 -04:00
061e79c295 welcome: Let's try this without server-side JS. 2025-07-16 17:57:15 -04:00
5edfe732b1 core: Make it possible to host a web page with no additional server-side JS. An experiment in supporting simplicity and increased ability to be publicly searched and archived. 2025-07-16 12:47:16 -04:00
a8f9b67f71 cleanup: Remove the /ebt endpoint. The connections tab is superior. 2025-07-15 18:18:35 -04:00
de7fbf1eb7 update: picohttpparser. 2025-07-14 21:10:02 -04:00
a51a3d7e43 update: lit 3.3.1. 2025-07-11 21:36:28 -04:00
433b3b1003 update: CodeMirror. 2025-07-09 18:59:03 -04:00
6703c5b584 ssb: Fix letterboxing/pillarboxing of images regardless of aspect ratio. 2025-07-09 18:53:27 -04:00
5f729efabe ssb: Load more messages at a time. 2025-07-09 18:35:51 -04:00
b2085b3f28 ssb: Try to keep the profile description from falling off the page. CSS, ugg. #126 2025-07-09 18:25:47 -04:00
2f893494b0 ssb: Show the number of accounts followed on the profile show/hide followed button. 2025-07-09 17:50:06 -04:00
e26af21f63 ssb: Disambiguate some sqlite errors better. Today I learned there are various cases it doesn't update the error message, and prepare can succeed and not produce a statement. 2025-07-09 12:36:58 -04:00
7e1d738f8d ssb: Don't show the connection buttons in the main bar if there's a sidebar. 2025-07-09 12:09:03 -04:00
199448e11e build: Back to building 0.0.33-wip. 2025-07-07 12:40:32 -04:00
fdaabab807 android: Suppress a setDatabaseEnabled deprecation warning. 2025-07-07 12:12:37 -04:00
ca4560c5c9 update: speedscope 1.23.0. 2025-07-07 12:11:59 -04:00
2478f3064d android: Don't draw behind the status bar. 2025-07-06 07:39:09 -04:00
e9b8b43e7c ssb: Minor formatting around the connection sidebar buttons. 2025-07-06 07:27:01 -04:00
951155f1b6 build: Use this apksigner workaround for reproducible builds: https://gitlab.com/fdroid/fdroiddata/-/issues/3299. 2025-07-02 20:35:51 -04:00
1b678175ef build: Missed a number. Let's build a 0.0.32.1 with the version bumps and release it on F-Droid to see that we can. 2025-07-02 19:34:33 -04:00
8eb1f40eec build: Only build docs once, and use the right number of jobs. 2025-07-02 19:12:08 -04:00
235887b3bf android: Bump android versions in the gitea action, too. 2025-07-02 18:59:29 -04:00
0b3d66dd48 android: Google says: 'App must target Android 15 (API level 35) or higher'. Bump SDK versions while I'm in here. 2025-07-02 18:53:04 -04:00
beb9ef3754 ssb: Add a missing 'Encrypt' label. 2025-07-02 18:30:35 -04:00
9f6a480736 ssb: Nudge around some space around the compose widget. 2025-07-02 18:29:02 -04:00
b3bac2927d ssb: Shorten the timed query output. 2025-07-02 18:21:40 -04:00
ef389f2ba2 ssb: The unread line can be used to make everything above read. 2025-07-02 18:19:34 -04:00
ef21dc6ae8 ssb: Use img title=, not alt= for more browser support. 2025-07-02 12:49:14 -04:00
6e55b6b49e ssb: Show sync/stay connected options on the sidebar when relevant. 2025-07-02 12:41:03 -04:00
db115ef1bd cleanup: Just assessing how much we need OpenSSL and noticed cruft. 2025-07-01 18:42:43 -04:00
678838dbd5 update: OpenSSL 3.5.1. 2025-07-01 18:32:19 -04:00
586f87625d ssb: Revise the message queries slightly to not include votes where it might just be noisy. 2025-07-01 12:50:02 -04:00
1542370f9b ssb: Let's try not tracking vote unread status. It's useful that the channel is there, but it's too noisy for me to want to mark it as read. 2025-07-01 12:41:25 -04:00
1f7d5968c7 update: CodeMirror. 2025-06-29 17:21:26 -04:00
39e51f7790 update: sqlite 3.50.2. 2025-06-29 17:20:50 -04:00
052663efbe ssb: Proof of concept to try to stay connected to a handful of peers. #130 2025-06-28 13:47:58 -04:00
8f84ff2611 ssb: Collapse contact group users to only icons. 2025-06-27 12:46:15 -04:00
37e1c5d97b core: Use crypto_generichash. We don't need to roll our own FNV32a. 2025-06-25 21:10:05 -04:00
cef526bcf3 ssb: Fix the tab cycling order. 2025-06-25 20:23:01 -04:00
6af36cafa9 ssb: Wait, I don't think we need to close any connections here. 2025-06-25 20:10:22 -04:00
fca859d93d ssb: Are we inadvertantly closing connections when an inner tunnel request ends? 2025-06-25 20:00:44 -04:00
2178300d8d welcome: +PeachCloud. 2025-06-25 18:49:43 -04:00
636bdcce6b build: Start work on 0.0.33. nix => 0.0.32. 2025-06-25 18:34:59 -04:00
94b7703ca9 build: Let's build 0.0.32. 2025-06-25 12:58:25 -04:00
a391dd1316 update: prettier 2025-06-25 12:57:29 -04:00
b6ba5211b7 update: CodeMirror. 2025-06-23 21:34:27 -04:00
8e8e130045 docs: Ready 0.0.32 release notes. 2025-06-23 12:52:31 -04:00
1f40bc1a0f core: Fix the one place where we called a JS function and didn't check for jobs to run as a result. Fixes getting stuck in the intro as a non-admin. 2025-06-22 18:53:20 -04:00
5437212222 build: Fix android. 2025-06-18 20:25:49 -04:00
a8ab845cd2 docs: Minor usage cleanup. 2025-06-18 20:13:16 -04:00
8cee6dc98b docs: Fix copy+paste lies. 2025-06-18 19:58:25 -04:00
70c2b73414 welcome: Introducing hermietildefriends.svg. 2025-06-18 19:43:24 -04:00
98013c4422 welcome: Direct to the latest release, obviously. 2025-06-18 19:16:34 -04:00
e9e22b762d docs: Make this format a little prettier-friendly. 2025-06-18 19:08:47 -04:00
620db19936 docs: Auto-generate a usage.md from the command-line interface. 2025-06-18 19:00:13 -04:00
94a79dd62c ssb: How did I not have this index? Makes channel unread queries significantly faster. 2025-06-18 18:26:11 -04:00
b56c3efde0 prettier 2025-06-18 12:37:41 -04:00
066827f8f1 ssb: Deconstruct and instrument the channels unread query. 2025-06-18 12:36:59 -04:00
c3b65d9cd8 update: CodeMirror. 2025-06-18 12:12:57 -04:00
a15b916b06 ssb: Only print broadcast failures once. 2025-06-16 22:26:23 -04:00
31d0a5c233 intro: Don't meddle with settings as non-admin. #129 2025-06-16 12:20:09 -04:00
140179e80a intro: Fix grammar. #128 2025-06-16 12:12:50 -04:00
53cba2d7e4 Move all but the deleting off of the writer when deleting blobs and messages. Trying to eliminate a period of unresponsiveness near launch. 2025-06-15 08:46:07 -04:00
e54312d3b8 ssb: Only admins are offered the option to enable peer exchange. 2025-06-14 20:27:19 -04:00
cadc27b7b5 ssb: Filter out invalid RPC flags. 2025-06-12 12:50:38 -04:00
388b829ec1 ssb: Allow unread status for the mentions tab. 2025-06-12 12:25:58 -04:00
67861f0f33 ssb: Add some options to encourage getting connected to the connections sidebar section. 2025-06-11 20:53:51 -04:00
a1f1eb34d5 ssb: sequence: Sequential 32-bit integer starting at 1. 2025-06-11 20:12:23 -04:00
2a6789063e ssb: Show/hide "Mark All Read" and the unread line more correctly. 2025-06-11 19:22:21 -04:00
cbf1273a55 ssb: Squeeze some blood from the following_perf stone. 2025-06-11 19:12:59 -04:00
8143a23ced format 2025-06-11 18:52:56 -04:00
3c17810747 welcome: Update the welcome page to be a bit more direct and relevant. #124 2025-06-11 18:39:15 -04:00
bea7a2e9ed core: Use JS_NewTypedArray. 2025-06-11 12:56:27 -04:00
2f0a2ac6b0 core: Prefer JS_AtomToCString() over JS_AtomToString() + JS_ToCString(). 2025-06-11 12:46:52 -04:00
18908b6b56 core: Ignore websocket errors when navigating away from the page (ie, Abnormal closure when switching apps). #60 2025-06-11 12:10:28 -04:00
b135a210cc core: Avoid trivial snprintfs. 2025-06-10 21:17:55 -04:00
3a2a829940 ssb: Disabling room support only disables the ability to tunnel through ourselves, not receive tunneled connections. 2025-06-10 20:36:59 -04:00
e56dd2dd2d core: Remove hitch tracking. Hasn't been an active problem, and the traces are sufficient to diagnose. 2025-06-10 12:44:56 -04:00
3f41a48bc7 ssb: Use message refs to get channel contents, not full-text search. Much faster for certain channels. 2025-06-09 12:16:09 -04:00
65ed53281a ssb: Shutdown hygiene. #108 2025-06-07 18:00:05 -04:00
1121557a2e update: CodeMirror. 2025-06-07 17:29:45 -04:00
d4a7b86ee7 ssb: Revise private scanning slightly. 2025-06-07 15:10:37 -04:00
626c18b04e ssb: Fix some correctly identified private messages not showing up in the private message feed. 2025-06-07 15:04:08 -04:00
bfa97ed7c7 log: Disable stdout buffering. I want to see output sooner in journalctl. 2025-06-07 13:24:55 -04:00
deae4d5367 ssb: Obviously close the readers before the writer. 2025-06-07 13:19:46 -04:00
899605c860 ssb: Actually prevent the sqlite WAL file from growing out of control by truncating when it grows to about 64MB of pages. 2025-06-07 11:39:31 -04:00
dc9a279991 ssb: Free sqlite3_exec errors. 2025-06-07 10:36:44 -04:00
2a53892581 update: sqlite 3.50.1. 2025-06-07 08:04:34 -04:00
6bef0eb764 prettier 2025-06-04 20:48:04 -04:00
462b40640c ssb: Don't show messages as unread when not in channels that allow unread status. 2025-06-04 19:56:02 -04:00
72e1b2025c core: Chasing shutdown issues some more. Logging. No more task promises once shutdown starts. #108 2025-06-04 19:47:54 -04:00
fc7c4b1257 docs: Add a quick doc of how connecting Manyverse to tildefriends.net is supposed to work. 2025-06-04 18:45:18 -04:00
6c22c59056 room: Fix multiple issues with the core room app. Good gravy, it's like I don't want people to connect. 2025-06-04 18:24:31 -04:00
94c2b1184f prettier 2025-06-04 18:01:39 -04:00
45231d703d ssb: Split recent votes into their own sidebar item as an experiment. #122 2025-06-04 18:00:46 -04:00
7882fcbe8f ssb: The red border is too much and stacks poorly. Let's try the same unread icon from the sidebar. 2025-06-04 12:24:04 -04:00
3bbc8c4d35 ssb: Consolidate the buttons around the compose UI. #122 2025-06-02 21:47:35 -04:00
8ae10dc80b ssb: Add some more visibility that you're above the unread line. #122 2025-06-02 21:24:03 -04:00
9b11c2c629 ssb: Expose a list of followed accounts on the profile page. #122 2025-06-02 21:06:25 -04:00
e2a231fb4a ssb: Consolidated contact message groups a bit too much. 2025-06-02 12:14:06 -04:00
8a9502d1f2 ssb: More correct/thorough channel status. 2025-05-31 17:55:49 -04:00
534438df63 ssb: The message_refs table had loads of erroneous entries. This fixes that and adds channel/hashtag refs. 2025-05-31 17:42:17 -04:00
45a4feec96 ssb: Fix a use of undefined. 2025-05-31 16:52:37 -04:00
aa7a32395e ssb: Expose last successful connection time for stored connections. 2025-05-31 16:33:48 -04:00
ab9f57f044 ssb: Support expanding profile images the same as other images. #122 2025-05-31 16:11:13 -04:00
4040d6aa08 ssb: Hide 'Mark as read' when we're not in a channel with a read status. #122 2025-05-31 15:23:07 -04:00
1c96f5c35e ssb: Slight tweaks to 'This message is not currently available.' 2025-05-31 15:20:55 -04:00
4d3e42812d ssb: Condense follows/blocks more, and support replies to them. #122 2025-05-31 15:17:07 -04:00
f7b3711d4f ssb: Bury placeholder message blob ids in a context menu. #122 2025-05-31 11:44:50 -04:00
2408e076ff ssb: Indicate connection status with some colors in the connections list in the sidebar. #122 2025-05-31 11:28:09 -04:00
6f71ffb477 ssb: Treat most plaintext votes as 👍. #122 2025-05-31 10:52:07 -04:00
214433f36a update: CodeMirror. 2025-05-29 12:39:31 -04:00
309b22732e update: sqlite 3.50.0. 2025-05-29 12:37:14 -04:00
6fe7687b2a docs: Update the screenshots. 2025-05-28 20:49:48 -04:00
a8cbf757ff build: Let's start work on 0.0.32. nix => 0.0.31. 2025-05-28 18:12:15 -04:00
4a4bedfe2b docs: Fix the changelog version. Hmm. 2025-05-28 12:28:33 -04:00
051291f725 build: Let's build 0.0.31. 2025-05-28 12:27:37 -04:00
d2b338095f update: CodeMirror. 2025-05-28 12:26:42 -04:00
899827a8f2 ssb: Rename broadcasts to something not worse. 2025-05-27 21:09:26 -04:00
5fcbe3d6a9 docs: Prep for 0.0.31. 2025-05-27 12:44:33 -04:00
a0a40e6cb2 ssb: Implement blob file export from CLI. #121 2025-05-27 12:30:24 -04:00
bb1190e3f8 intro: Ugg, CSS. 2025-05-22 12:00:33 -04:00
0a3baed1da intro: Wordsmith some more, and fiddle with style. 2025-05-22 11:49:58 -04:00
4931c489ed ssb: Dispatch blob wants immediately when added as the result of a web request. 2025-05-22 10:56:30 -04:00
996f9abaa2 ssb: Suppress the 'set your profile' banner if we're still loading abouts. 2025-05-22 10:44:41 -04:00
08c097e176 ssb: Fix a source of stale user information. 2025-05-22 10:26:59 -04:00
daa861a98b test: Test the intro flow. 2025-05-22 10:19:55 -04:00
a25d08fd76 intro: Skipped a step. 2025-05-22 07:04:57 -04:00
392d31cc53 format 2025-05-21 22:16:52 -04:00
92926fa8df ssb: Chasing intermittent SQLITE_ERROR with multiple processes accessing the database. Switching to sqlite3_prepare_v2 for better errors, but maybe this is also the solution? 2025-05-21 22:05:33 -04:00
61ae9ae465 intro: Set an emoji, obviously. 2025-05-21 18:51:35 -04:00
89622697d5 intro: The default app is intro, and complete intro changes the default app to ssb. 2025-05-21 18:48:29 -04:00
17694f5646 update: CodeMirror. 2025-05-21 18:20:01 -04:00
5a1303149f intro: Add an intro app that aims to ease in completely new users. Needs some clean-up and to be hooked into the flow. #19 2025-05-21 18:08:19 -04:00
8a0e190a86 ssb: Clean up outdated dynamic blob_wants_cache entries. 2025-05-21 17:44:00 -04:00
0d7dfd8c9e ssb: Get the 'encrypt to' input in line with everything else style-wise. 2025-05-18 07:55:10 -04:00
f979ff7050 ssb: Speculation on why I sometimes don't see names update. 2025-05-17 20:10:15 -04:00
e3fcdea362 ssb: Add entries to the blob wants cache when requesting a missing blob from the web. #120 2025-05-17 12:46:47 -04:00
476fec2757 update: w3.css 5.02. 2025-05-14 22:19:53 -04:00
53c215399b ssb: Ugg, better safe than sorry. 2025-05-14 20:57:42 -04:00
2c330802da ssb: More account info fixes. 2025-05-14 20:56:13 -04:00
851d7046ea ssb: Fix failure to gather about information sometimes. 2025-05-14 20:39:06 -04:00
c0019d7246 format 2025-05-14 20:33:02 -04:00
7688e4d3a8 ssb: I tried smarter data structures in my own code, but a better database index shows better performance for determining follows. 2025-05-14 20:13:14 -04:00
ef58749ce3 perf: Make -t following_perf respect the usual db path rules. 2025-05-12 12:41:13 -04:00
35941a7ddc perf: Add a quick test to measure ssb.following performance. 2025-05-12 12:32:41 -04:00
1f2664e5a8 ssb: Reduce redundant work in ssb.following. 2025-05-11 22:20:16 -04:00
35656a5c34 prettier 2025-05-11 21:54:53 -04:00
799f22e989 ssb: The updated fetch_abouts means that image JSON is now a string we need to handle in tf-user / tf-profile. 2025-05-11 21:42:48 -04:00
e226a37251 prettier 2025-05-09 19:54:12 -04:00
8e3bc9d700 ssb: Now add back the about cache, and load times are getting good. 2025-05-09 13:25:05 -04:00
58c3e6c2ab build: Fix a -fanalyze issue in the tests. 2025-05-09 07:55:39 -04:00
0dc148bfea ssb: Don't forget the about info we know as we reload. 2025-05-09 07:35:52 -04:00
3eff1b08a9 ssb: Let about info load in its own time. 2025-05-09 07:27:35 -04:00
02d789471f ssb: Oops, filter on relevant accounts. 2025-05-08 12:58:57 -04:00
d367d47c4d ssb: Switch to a more brute force about-gathering approach. I think if I start with this and avoid querying all known accounts up-front, we will be plenty fast. 2025-05-08 12:39:26 -04:00
c93b8fc045 ssb: Show overall unread status on the hamburger menu. 2025-05-07 19:59:39 -04:00
eb9377e21d ssb: Add settings for broadcast and discovery. 2025-05-07 19:05:57 -04:00
a1764eee42 update: CodeMirror. 2025-05-07 18:37:23 -04:00
86ef74e20d test: More CLI tests, and make -u optional for publish, too. 2025-05-07 18:31:58 -04:00
4de53b9926 core: sqlite checkpoint tweaks. Switch the periodic checkpoint to passive mode. Truncate mode was painful on mobile. I'm seeing it succeed often enough that we don't grow indefinitely. Also fix the print. 2025-05-07 18:15:33 -04:00
99a195a3fd update: sqlite 3.49.2. 2025-05-07 18:00:30 -04:00
f1ced31f69 ssb: Faster loads by encouraging the right queries with CTEs in fetch_about. 2025-05-07 17:52:02 -04:00
b3cedf2baa ssb: Begin to add some CLI tests. 2025-05-06 12:49:57 -04:00
3bf19fabda ssb: Don't require -u for the private command. #119 2025-05-05 21:51:38 -04:00
cf81ebe8ad ssb: emoji-fy other cases of some reactions. 2025-05-05 21:32:40 -04:00
278b5566a1 ssb: Remove some unintended whitespace at the bottom of messages. 2025-05-05 21:09:58 -04:00
e8c1390f09 ssb: Reply arrow that shows up better on my phone. 2025-05-01 12:38:11 -04:00
3c04abda45 ssb: More shutdown correctness. #108 2025-04-30 12:35:40 -04:00
2597f99ccf ssb: Get recent reactions up front so that we don't need to delay showing the dialog for them. 2025-04-29 20:48:47 -04:00
9d3a07c1cf build: OpenBSD 7.7's SSL matches these signatures again. 2025-04-27 17:29:59 -04:00
bdfd8925b5 ssb: Remove some extra margin that slipped in. 2025-04-27 10:18:37 -04:00
1a4d1985f4 core: Revisit drag+drop into the editor. Works better but not as I intended. Use it just to update the screenshot in the welcome app. 2025-04-27 10:18:20 -04:00
6273f3ea53 ssb: Make the connections sidebar header also a link to the connections tab. 2025-04-26 18:53:08 -04:00
5bdc6fa471 update: CodeMirror. 2025-04-26 18:47:04 -04:00
3ba41291db update: QuickJS 2025-04-26. 2025-04-26 18:30:56 -04:00
0867811952 update: libuv 1.51.0. 2025-04-26 09:06:37 -04:00
8d961cd805 ssb: Indicate that content warnings can be expanded. 2025-04-24 12:35:36 -04:00
97cea7b40b build: Attempt to include native debug symbols in the Android .aab file. 2025-04-23 20:47:31 -04:00
4106834db8 build: nix flake update did this (but really it was nix --extra-experimental-features nix-command --extra-experimental-features flakes flake update). I don't know how any of this actually works. 2025-04-23 18:36:28 -04:00
a4a8f7cab2 build: Let's start work on 0.0.31. 2025-04-23 18:33:28 -04:00
9e209ee800 build: nix => 0.0.30. 2025-04-23 18:19:58 -04:00
ddfa84f040 docs: Changlog formatting. 2025-04-23 17:35:16 -04:00
6b3a6ec7c1 httpd: Don't overspecify the redirects. 2025-04-23 12:47:21 -04:00
4d037c02bf build: Let's build 0.0.30. 2025-04-23 12:29:42 -04:00
deaeab10d8 update: CodeMirror. 2025-04-23 12:29:02 -04:00
2a5375b1e7 core: Don't start new tasks as we're shutting down. #108 2025-04-20 18:26:44 -04:00
e7a03e3283 ssb: Slightly faster channel query. 2025-04-20 17:43:53 -04:00
efb3a12dcc ssb: Make the reactions list dialog a bit more compact/concise. 2025-04-20 09:35:39 -04:00
3830d695d7 docs: Getting 0.0.30 ready. 2025-04-20 08:50:10 -04:00
f36620927b ssb: Chasing some failed blob stores. Bumping some sqlite limits seems to have actually helped? 2025-04-19 21:40:15 -04:00
5423cbd628 import: Avoid log noise every launch when apps haven't actually changed. 2025-04-19 21:08:23 -04:00
abde709e54 prettier. 2025-04-16 19:27:24 -04:00
27f2d319ab storage: Faster. 2025-04-16 19:21:57 -04:00
66234b14bc test: Add some coverage of storing and resetting permissions by clicking the approve/deny/remember/reset buttons. 2025-04-16 12:32:34 -04:00
6a9167e565 ssb: More logging to help track down an issue I've seen on rare occasion when saving app changes from the editor. 2025-04-15 12:49:40 -04:00
3c60f8ca06 ssb: Whitespace around nested messages. 2025-04-15 12:42:13 -04:00
c26bf5c112 core: Setting and resetting user app permissions was all sorts of broken. 2025-04-14 12:41:35 -04:00
41cbde934a ssb: Layout the message expand/collapse buttons better. 2025-04-13 21:38:56 -04:00
946941d95e format 2025-04-13 21:18:28 -04:00
50f0104239 ssb: Add size to messages_stats. Forces an already-necessary rebuild and provides information I'm keen on having readily available. 2025-04-13 21:16:26 -04:00
40fa7edadf ssb: Show message count when viewing an account profile. 2025-04-13 20:48:55 -04:00
d6926569c6 verify: Add an option to dump a specific message in the format that its signature validates as well as a hex representation of the bytes for good measure. 2025-04-13 13:28:48 -04:00
a8bba324ca ssb: Fix removing a mention discarding a draft. 2025-04-13 07:53:15 -04:00
5bba5776b3 core: Get rid of the httpd JS object. Potential progress on #108. 2025-04-12 09:43:55 -04:00
8104f6f228 ssb: Fix and test the messages_stats trigger. 2025-04-12 08:40:36 -04:00
3f4738e593 ssb: Fall back to hostnames for connections in the sidebar. 2025-04-12 07:05:37 -04:00
1516e17f5d update: Lit 3.3.0. 2025-04-11 19:32:26 -04:00
676d2702b7 ssb: Read back-pressure on a tunnel connection affects the parent connection. This was causing spurrious disconnects. 2025-04-11 19:29:10 -04:00
5d39548964 ssb: Why is this faster? 2025-04-09 22:47:23 -04:00
67d458bd38 ssb: prettier. 2025-04-09 22:15:51 -04:00
d9684c7d62 ssb: Fix culling feeds again. 2025-04-09 22:15:01 -04:00
5a818d2119 ssb: More incremental profile loading? 2025-04-09 22:08:33 -04:00
6f96d4ce65 ssb: More different feed culling logic. 2025-04-09 20:45:23 -04:00
f72395756a ssb: If things time out because we're following a million accounts...recover ungracefully. 2025-04-09 20:02:16 -04:00
38d746b310 ssb: Dust off the verify command. 2025-04-09 19:32:38 -04:00
f7270987ea http: Trying to track down a rare 404 I get on save. 2025-04-09 19:08:28 -04:00
6f565c0f0a ssb: Move reply and react to the message % menu. 2025-04-09 19:02:02 -04:00
7f252e79b6 ssb: Faster channel loads. 2025-04-09 18:50:14 -04:00
ba2bb17638 ssb: Gather all the votes for messages seen. 2025-04-09 18:29:37 -04:00
bc7c658293 update: CoreMirror. 2025-04-09 12:28:56 -04:00
4d84e69bb5 update: libbacktrace. 2025-04-09 12:26:44 -04:00
03fac74908 update: c-ares 1.34.5. 2025-04-09 12:24:55 -04:00
5252ff1ecf update: OpenSSL 3.5.0. 2025-04-09 12:22:12 -04:00
20100d3fd4 ssb: Oops, that event was Firefox-specific. 2025-04-07 22:35:40 -04:00
dd3b2656ad ssb: Message % menu tweaks. Show on click. Hide when clicking off. Bury the message ID further. 2025-04-07 21:39:14 -04:00
657f25e22b ssb: Bring back the date. Whoops. 2025-04-05 22:05:26 -04:00
8be354fc49 ssb: It is troubling to me that querying like this is so much faster. 2025-04-05 18:40:00 -04:00
e574758340 ssb: Make a message % context menu with some new options to begin to declutter the message view itself. 2025-04-05 17:53:47 -04:00
40cf519492 http: I caught an http listener lingering on shutdown. Clean them up more correctly. Possible progress on #108. 2025-04-05 09:53:47 -04:00
0e4fda54e9 fdroid: Lean into SSB in the description. 2025-04-03 12:31:59 -04:00
868f91e1ef docs: Noticed some errors in the readme. 2025-04-02 20:01:26 -04:00
b9000c154f apps: Add a very wip web app. 2025-04-02 18:07:25 -04:00
894c72a82f ssb: Still chasing faster loads of the default news query. 2025-04-02 12:44:17 -04:00
c128cfc25c ssb: This makes the channels query sub-second for me. 2025-04-01 19:50:13 -04:00
36c88b463c buttfeed: Add PatchFox. 2025-04-01 08:13:54 -04:00
8a66e74074 ssb: Faster *and* correct. I think. 2025-03-30 13:18:16 -04:00
ea60b165da ssb: Had my refs backward. 2025-03-30 12:27:36 -04:00
1011e0026b ssb: Rearrange indexes for an overall faster load on mobile for me. Tidy up some things along the way. 2025-03-30 12:16:23 -04:00
9462521287 update: CodeMirror. 2025-03-27 12:37:13 -04:00
576022c41a build: 0.0.30-wip, let's gooooo. 2025-03-26 20:19:28 -04:00
70c38b7ea8 build: nix => 0.0.29. 2025-03-26 18:57:05 -04:00
36370f2dea build: Fix this dependendy test. 2025-03-26 18:48:49 -04:00
db9bf7f7bd build: Let's build 0.0.29. 2025-03-26 12:28:06 -04:00
fa7aef0c37 build: Fix make warnings on macos. 2025-03-25 19:31:25 -04:00
b135ea17f6 ssb: Let's make sure these are never null again. 2025-03-22 22:17:03 -04:00
4b1643bc47 ssb: Hook up a busy handler to give checkpointing a chance of working, and fix fundamental bugs with the messages_stats table. How did this ever work? 2025-03-22 08:58:43 -04:00
240a8ce9c7 ssb: All initial population in transactions for correctness. 2025-03-21 20:52:27 -04:00
8928e8722b ssb: Notifying apps of every single message added isn't practical for big replications. Only notify on the completion of each chunk. Update the tests. The apps need some treatment, but refreshing works around any issues for now. 2025-03-21 20:45:49 -04:00
d692734e55 ssb: Prefer getting last message sequence from the messages_stats table if we don't need the message id. Trying to chip away at things I see in profiles. 2025-03-20 12:53:38 -04:00
50197198b4 update: Missed one instance of w3.css. 2025-03-20 12:41:20 -04:00
1ee1107c93 update: w3.css 5.0.1. 2025-03-19 20:51:51 -04:00
cf90533b6c ssb: Make these queries match the index more pedandically, if only for consistency. 2025-03-19 20:31:42 -04:00
f0211f621e ssb: That's just a weird formulation of this query. I saw it in a profile. 2025-03-19 19:09:29 -04:00
d9693af89b build: Some prep for v0.0.29. 2025-03-19 18:37:17 -04:00
13722232fb ssb: Don't let GC saturate the main thread during replication. 2025-03-19 07:47:10 -04:00
0bcb033349 ssb: This seems like it would explain replication stalls. 2025-03-19 06:46:34 -04:00
e92c439724 ssb: Commit messages in transactions for performance. 2025-03-18 12:49:47 -04:00
7f34b585d3 ssb: Don't count messages we're not intending to replicate toward progress. 2025-03-17 21:13:20 -04:00
d7e9fd918a ssb: Idea: ebt clocks shouldn't go backward. 2025-03-17 20:49:55 -04:00
9899c0c5e2 ssb: Try to keep these stats from going negative. 2025-03-17 12:29:30 -04:00
c50de0b0f0 ssb: Testing a theory that message sends are piling up. 2025-03-17 12:27:16 -04:00
bb7d2d7ae0 ssb: Update ebt received value on the fly. 2025-03-16 22:04:06 -04:00
862d172ca8 ssb: Pub blobs in the idle queue, too. I'm confused why things aren't moving like I'd expect. This seems more fair. 2025-03-16 19:30:35 -04:00
3671051d0e cli: Show usage and error on unexpected arguments. 2025-03-16 12:45:54 -04:00
223e20cbbc ssb: Fix the tests. 2025-03-16 11:52:11 -04:00
9af4312561 ssb: Attempt to show EBT replication progress in the connections tab, mainly to help track down bugs, which there are. 2025-03-16 11:39:28 -04:00
934e40240e build: Fix some haiku make warnings. 2025-03-15 17:46:25 +00:00
edb1980387 ssb: Address a -fanalyze issue. #114 2025-03-15 13:44:02 -04:00
bb7b04013f ssb: Resolve a DB-related naming collision I caused better. 2025-03-12 20:49:48 -04:00
26a3007268 http: We can bind to localhost if we do it right, apparently. 2025-03-12 20:23:37 -04:00
5de2b09596 ssb: Obviously remove from blob_wants_cache when a blob is added. 2025-03-12 18:23:38 -04:00
3660577a23 ssb: I made blob_wants_view too slow. Let's make a real table updated by triggers. 2025-03-12 18:17:11 -04:00
98b4c7cf04 http: Disable binding to localhost on mobile until I can solve 'address not available'. 2025-03-11 21:36:55 -04:00
427a7b8d25 ssb: Request blobs referenced by messages, as an experiment. Recursively, too. 2025-03-11 19:02:48 -04:00
67b84830cd debug: Add an /ebt endpoint to examine the state of EBT clocks. Prevent invalid identities from getting in there, now that I see them. 2025-03-09 11:28:47 -04:00
973cd53266 security: Make mobile listen on localhost by default. I did not intend to leave it open. 2025-03-08 20:40:03 -05:00
1afdbe6932 build: Let's include speedscope again.
We're no longer trying to fit into a 5MB APK or anything.
2025-03-07 12:48:35 -05:00
942f582329 trace: Fix a crash and source of invalid json. 2025-03-07 12:37:02 -05:00
951a80389a update: CodeMirror. 2025-03-05 20:32:56 -05:00
b7ecfc9925 ios: Fix file upload. This is the last thing I would have expected. #109 2025-03-05 19:48:39 -05:00
59e389d793 ios: Add rough back/forward/refresh buttons. #111 2025-03-05 18:56:06 -05:00
2ec047cc00 build: Appease various doxygen versions. 2025-03-04 12:46:12 -05:00
ee33f54745 ssb: Don't send EBT clocks that weren't asked for. Fixes more replication-related warnings. 2025-03-04 12:19:52 -05:00
7a79534ca8 docs: Add some words about upgrading. 2025-03-02 21:21:04 -05:00
a74a9fc821 ssb: Clean up some muxrpc-related warnings. 2025-03-02 19:43:50 -05:00
e2c388b9db cleanup: Stray file again. 2025-03-02 19:29:27 -05:00
0f573ce09e ssb: Move identity info gathering to db. 2025-03-01 20:44:52 -05:00
bc70e41b7c ssb: prettier. 2025-02-27 20:17:24 -05:00
f500e14aa3 ssb: Make the connections tab a bit less scary. 2025-02-27 20:04:38 -05:00
8b47938238 perf: Make promise stack trace collection opt-in. 2025-02-27 19:41:21 -05:00
8912212d8e build: Fix 32-bit. 2025-02-27 19:10:48 -05:00
6a346bf940 http: Move websocket send to http, obvs. 2025-02-27 19:07:01 -05:00
b5bdae4611 js: prettier 2025-02-27 15:00:37 -05:00
6590da5793 js: Just delete some placeholder comments around code that I am trying to delete. 2025-02-27 15:00:02 -05:00
eb2b426ec7 js: Clean up some oddness in old code as I struggle to replace it. 2025-02-27 14:28:07 -05:00
4864a0411f js: This RPC info does not need to be global. 2025-02-27 13:52:24 -05:00
68590cae33 http: Avoid a potential null dereference. 2025-02-27 11:25:38 -05:00
abf2bbaec2 js: Very minor trimming. 2025-02-27 10:01:59 -05:00
0da7e2722f js: Add a place to start moving imports to C. 2025-02-27 09:58:15 -05:00
60d4b06057 http: Prevent reentering tf_http_destroy. 2025-02-27 08:52:42 -05:00
4c3df34950 build: Let's start work on 0.0.29. 2025-02-26 20:04:17 -05:00
7737e60b52 build: ios => CFBundleVersion=10. 2025-02-26 19:44:00 -05:00
71e816bc13 build: nix => 0.0.28. 2025-02-26 19:43:20 -05:00
c74f90ef04 core: Fix stock apps not being loaded/updated. 2025-02-26 18:54:00 -05:00
26cb7e5a17 ios: Redid icon stuff. 2025-02-26 18:27:54 -05:00
2bad6672d8 ios: Generate Assets.car out of an overabundance of caution that F-Droid is going to complain about this as a binary file. 2025-02-26 12:43:42 -05:00
71c4011526 build: Let's prepare a 0.0.28 release. 2025-02-26 12:07:54 -05:00
5e81078f59 update: CodeMirror. 2025-02-26 12:05:07 -05:00
31b78e74df ssb: Following calculation fixes. It was not handled appropriately if an account was encountered multiple times with decreasing depths. 2025-02-25 21:52:15 -05:00
2ff689aab0 buttfeed: De-duplicate updates by link. 2025-02-24 12:16:31 -05:00
0a6f0ed3f7 ssb: Audit timer use that might cause unnecessary delays during shutdown. 2025-02-23 13:07:36 -05:00
bfec46673d ssb: Test an experiment that blobs are getting priority over messages during initial sync. 2025-02-22 11:57:57 -05:00
3a16614c72 docs: Remember to update the latest_release tag. 2025-02-22 11:23:36 -05:00
9c9efb845c ssb: Schedule the clock for reevaluation any time new messages are added. I think this will improve initial replication. 2025-02-21 22:30:14 -05:00
6192f1b94d room: Fix the room app port number multiple ways. 2025-02-21 22:05:28 -05:00
ce451b2449 cli: Fix the return value for get_profile. 2025-02-21 19:58:01 -05:00
8534e16469 docs: +sidebar. 2025-02-19 20:20:21 -05:00
f6cc6f2eae docs: Minor prep for 0.0.28. 2025-02-19 19:54:45 -05:00
0904425221 update: CodeMirror. 2025-02-19 19:04:59 -05:00
a729886522 update: sqlite 3.49.1. 2025-02-19 18:59:45 -05:00
e5dfedc7d1 ios: Drop min ios version to 14. Generate more of the Info.plist. We don't use non-exempt encryption. 2025-02-19 18:49:53 -05:00
f02423d084 ios: Now with more sed. 2025-02-18 21:46:04 -05:00
8f4b6e83eb ios: This fixes the build on macos and runs in the simulator. 2025-02-18 21:28:03 -05:00
3029919553 ios: Might as well generate the CFBundleShortVersionString from that other version. 2025-02-18 20:58:16 -05:00
ac67db0591 Merge pull request 'nix: fix build instructions fuckup' (#107) from tasiaiso/tildefriends:tasiaiso-fix-fuckup-holy-shit into main
Reviewed-on: cory/tildefriends#107
2025-02-18 19:26:56 -05:00
1e08838f5b ios: I was able to get an iOS build to app store connect with this. Sanity lost. 2025-02-18 19:03:25 -05:00
d814f7ee77 nix: update flake lockfile, fix update procedure 2025-02-18 22:41:00 +01:00
7edfb9d386 docs: I forgot to do this last release. 2025-02-17 21:16:38 -05:00
6928d6caba cleanup: prettier. 2025-02-16 15:52:27 -05:00
1a626875cf build: Link warning on macos. 2025-02-16 15:13:45 -05:00
6eb3b64334 build: Fix android aab build. 2025-02-16 14:41:14 -05:00
09f3595e93 ssb: Use the right shape for your own profile image. 2025-02-16 14:37:45 -05:00
11e89622d4 security: Respect the autologin setting better. 2025-02-16 14:07:14 -05:00
0fa8acc264 ios: Untested, but attempt auto-login. 2025-02-16 13:42:07 -05:00
afc7c64ed8 android: Fix launch args. 2025-02-16 13:41:26 -05:00
c794c1b885 admin: Global settings can be specified on the command-line. Removed some previous, less thorough ways of configuring things. #102 2025-02-16 13:37:25 -05:00
6247529799 build: Fix F-droid icon reference. 2025-02-16 11:49:45 -05:00
ad3eedc1fb android: Avoid the extra background on the icon, and set a different color for F-droid builds. 2025-02-16 11:43:27 -05:00
1cfac3cae6 ssb: Allow otherwise unrecognized incoming connections when not talking to strangers if they have a valid invite. 2025-02-16 08:45:08 -05:00
478bcd5d13 ssb: New incoming connections replace old ones for the same id. 2025-02-16 08:19:40 -05:00
6932da69b3 update: speedscope 1.22.2. 2025-02-15 21:08:46 -05:00
857f47bf55 ssb: Make the invite.use reponse flags match the docs exactly, and add some connection state paranoia. 2025-02-15 20:52:39 -05:00
373d742751 ssb: The docs show the response to invite.use() wrapped in {key: ..., value: ...}, so do that. 2025-02-15 08:19:23 -05:00
575622c522 ssb: Did that from the wrong thread. 2025-02-13 21:37:40 -05:00
a3e86ccb1d update: CodeMirror. 2025-02-12 19:43:19 -05:00
e491798ff1 auth: Auto-login fixes. 2025-02-12 19:37:06 -05:00
15df4ac236 auth: Skip auth on mobile. #97 2025-02-12 19:25:05 -05:00
017a74c4e4 ssb: Reload the profile on follow/unfollow/block/unblock. 2025-02-12 18:20:44 -05:00
6e5b1127f3 ssb: If we don't accept the invite, give up on the connection. 2025-02-12 18:08:40 -05:00
95f4f88949 ssb: Non-expiring invites were still broken. 2025-02-12 18:03:53 -05:00
92858882d7 update: libbacktrace. 2025-02-11 20:53:57 -05:00
4cba95ec8c update: CodeMirror. 2025-02-11 20:52:13 -05:00
bbb2f89d85 update: OpenSSL 3.4.1. 2025-02-11 20:48:41 -05:00
88213038b2 build: Link with -gz=zlib to make some of these distributables megabytes smaller. 2025-02-11 20:42:22 -05:00
cc18b41e76 update: sqlite 3.49.0. 2025-02-10 22:05:02 -05:00
f16127a238 build: Fix mixing directory for lipo targets. 2025-02-10 21:28:22 -05:00
2a6ecfaede build: Set an LD_LIBRARY_PATH for macos on linux. 2025-02-10 20:57:14 -05:00
cf4a09bf03 build: Use clang-19 on the worker, and cleanup some symlinks I commited in error. 2025-02-10 20:23:54 -05:00
20e60262fd build: Some clangs are more strict than quickjs. 2025-02-09 21:46:17 -05:00
9e3928c976 build: Some clangs are more strict about this. quickjs is not. 2025-02-09 21:45:05 -05:00
95d8768545 build: Try to get CI building for macos. 2025-02-09 20:47:20 -05:00
8679d09040 build: Build a fat macos binary. 2025-02-09 08:13:13 -05:00
4cc5c6acb3 build: On second thought, split the cross-build of macos into x86_64 and arm. 2025-02-06 12:48:17 -05:00
6f9b548b1a build: There seems to be a way to build for macos from Linux. Neat. 2025-02-05 20:50:41 -05:00
fbff3386a9 update: CoreMirror. 2025-02-05 18:58:03 -05:00
e5899fca58 ssb: Use the cache of private messages we built for the unread notification to actually show private messages. Still needs some work, but it's something. 2025-02-05 18:41:37 -05:00
dddec489b9 ssb: Results of staring at unexpected shs disconnections: wire up tunnel.endpoints and blobs.createWants more correctly/thoroughly. And add slightly more context when deliberately disconnected from the remote side. 2025-02-04 21:32:22 -05:00
b4049eaeaa welcome: Feeling fancy. Might revert later. 2025-02-04 12:53:04 -05:00
b3fd724b5a docs: Some updates to the README. 2025-02-04 12:39:59 -05:00
aca25be86a docs: Minor cleanup/normalization. 2025-02-03 12:39:23 -05:00
0f8cbdac57 docs: Bring back docs as a directory. Long live https://docs.tildefriends.net/ 2025-02-02 22:39:55 -05:00
630219d667 docs: Stop using docs as a submodule. It discouraged pull requests. 2025-02-02 22:39:11 -05:00
fef268e434 build: Oops. 2025-02-02 22:21:16 -05:00
e18dcf3a48 build: Maybe ssh? 2025-02-02 22:18:05 -05:00
e019320146 build: Forgot the actual rsync options I wanted. 2025-02-02 21:55:57 -05:00
65b31e14f9 build: +rsync. Formatting yaml. 2025-02-02 21:51:18 -05:00
9cb5cbcde9 build: OK, that fail's on me. 2025-02-02 21:47:05 -05:00
25914ff5a7 build: c'mon 2025-02-02 21:44:05 -05:00
c4af799279 build: Let's try to put docs somewhere. 2025-02-02 21:40:29 -05:00
3f8f0e14f4 build: Make a directory for a thing that fails intermittently?? 2025-02-02 17:12:03 -05:00
5414b30e7f build: Resolve some zsign/cmake warnings. 2025-02-02 17:07:59 -05:00
7aee897c1b cleanup: No longer need form parsing in JS. 2025-02-02 16:52:44 -05:00
4060f9cc11 build: jarsigner -> apksigner to suppress a warning. 2025-02-02 16:38:55 -05:00
5b526cbf5b build: Clean up / consolidate some OpenSSL options now that there is only one place. Also: deadstrip better? 2025-02-02 16:19:46 -05:00
f5065ff42b build: Indicate that we always use system OpenSSL on Haiku and BSD. 2025-02-02 21:08:46 -05:00
86b5546f5f build: Get rid of the ios-specific OpenSSL build script. Now there is only one. 2025-02-02 15:56:06 -05:00
43ae2a293b build: Fix cross-compiling for arm. 2025-02-02 15:55:12 -05:00
b8ddbd4255 build: Remove the mingw-specific OpenSSL build script. 2025-02-02 15:21:38 -05:00
87cdba1db8 build: Replace the android-specific script to build OpenSSL with the shared one. 2025-02-02 15:16:51 -05:00
df83187e33 cleanup: Remove File.writeFile(). It is no longer used or necessary. 2025-02-02 14:52:25 -05:00
eccdbf29ab build: Move built openssl to out where it belongs. 2025-02-02 14:39:36 -05:00
28d181f8bc build: Fix. 2025-02-02 14:28:40 -05:00
d386daf2ff build: Rename the macosdebug/macosrelease targets to debug/release. #101 2025-02-02 14:08:27 -05:00
0c1e116c1e build: More concise build context. 2025-02-02 13:04:22 -05:00
bb0ed67827 httpd: Move starting the http server into C. 2025-02-02 12:27:06 -05:00
b111d06851 ssb: I think this fixes missing a range in some circumstances. 2025-02-02 10:14:14 -05:00
79388845ea build: Remove the dependency on zsign on macos. 2025-02-01 22:30:35 -05:00
99faef2e77 build: valid bash? 2025-02-01 19:59:18 -05:00
21fffbfe10 build: Valid yaml? 2025-02-01 19:58:28 -05:00
64fb9f0c2a build: +zlib. 2025-02-01 19:55:02 -05:00
a42e0bef2c build: The journey continues. 2025-02-01 19:15:50 -05:00
45a09006e1 build: Build zsign for the sake of the ios build. 2025-02-01 18:52:20 -05:00
240484be4c build: Fix docker build, and speed it up a bit? 2025-02-01 18:19:44 -05:00
22f4d115e3 build: Finally ios is happy. Now let's un-break docker. 2025-02-01 18:07:56 -05:00
32920e0e5d build: Still trying to get ios to build on this worker. 2025-02-01 17:58:55 -05:00
f03a5918d1 build: Apparently we need file. 2025-02-01 17:19:28 -05:00
dd1870b52a build: Nope, OpenSSL assumes this directory is SDKs, so it is SDKs. 2025-02-01 16:51:10 -05:00
f0c1a8f98f build: Minor cleanup and a disappointing workaround for iOS cross-compiling toolchain issues I don't understand. 2025-02-01 16:25:23 -05:00
0c181d7e7c build: Maybe maybe maybe. 2025-02-01 14:55:26 -05:00
e3dc0e833a build: Some docs say the syntax is like this. 2025-02-01 14:17:15 -05:00
6de875edea build: Print some configuration that is confusing me. 2025-02-01 13:38:52 -05:00
986a55173f build: I think CI can cross-build ios. 2025-02-01 12:50:31 -05:00
59c8cabf02 build: fix? 2025-02-01 11:39:58 -05:00
a33b9fab07 build: I misunderstood these makefile conditionals. 2025-02-01 11:32:01 -05:00
f8a725e1e7 build: ios dist is conditional on having the SDK. 2025-02-01 11:02:21 -05:00
b3ab3af01b build: dist uses curl. 2025-02-01 10:10:59 -05:00
79f9463e56 build: Can I get CI to artifact the dist files? 2025-02-01 09:49:49 -05:00
4257b2ed51 ssb: Unknown message types need some padding. 2025-02-01 09:41:22 -05:00
6488ab60ec build: Docker build no longer needs libssl installed. 2025-02-01 09:40:34 -05:00
18bd3dfcf9 build: So close. +docker? 2025-02-01 09:00:06 -05:00
ec114e160d build: on node:23-bookworm-slim 2025-02-01 08:34:42 -05:00
de033af18f build: Oh, now I can use a normal image, maybe with a more reasonable compiler? Also, update libbacktrace. 2025-02-01 08:31:33 -05:00
e971c6fcb7 build: Go home, -fanalyze, you're confused. 2025-02-01 08:14:11 -05:00
d3c465391c build: +zip 2025-01-31 21:47:24 -05:00
f1fa19593d build: Better system OpenSSL logic? 2025-01-31 21:43:18 -05:00
60b6f9c73e build: +build-essential 2025-01-31 21:32:17 -05:00
55b95ddecb build: +make 2025-01-31 21:27:14 -05:00
0800a251b2 build: +unzip 2025-01-31 21:23:22 -05:00
82cf7a80eb build: Maybe I don't need sudo here? 2025-01-31 21:19:22 -05:00
379f3d12eb build: Maybe we can just install the git we need? 2025-01-31 21:15:19 -05:00
e52972d4d4 build: Version shenanigans? 2025-01-31 21:11:35 -05:00
1a0ca4dec2 build: I guess this requires node? 2025-01-31 21:08:57 -05:00
0bac9d8d5a build: Can I specify the image we build on like this? 2025-01-31 21:07:01 -05:00
a9608363c5 core: Don't actually start the http server unless we've specified a port or are asking for a port to be determined and written to file. 2025-01-31 20:57:59 -05:00
f1a2c5ae8e ssb: Silence some persistent broadcast noise. 2025-01-31 20:45:35 -05:00
192a81ede7 core: As an experiment, handle running from a subdirectory (generally out/debug or out/release, since people tend to do that). Remove unused File.stat(). 2025-01-31 20:37:14 -05:00
916aa5abbd cli: Fix create_invite's expire option. 2025-01-30 22:16:06 -05:00
d4a5cc6eee build: Let's start work on 0.0.28. 2025-01-30 12:46:52 -05:00
d19605cc8d build: Fixes nix. Now everybody is happy. 2025-01-30 12:35:43 -05:00
8d529327a4 build: Let's go back to nix trying to use its own OpenSSL. 2025-01-30 12:31:12 -05:00
697e2f2071 build: Nope, I don't understand nix. 2025-01-29 20:53:09 -05:00
f7fb112f21 build: I don't understand this nix error. 2025-01-29 20:32:35 -05:00
b2c0211190 build: nix? 2025-01-29 20:28:26 -05:00
c59e0ea6e5 build: Let's release 0.0.27. 2025-01-29 19:58:31 -05:00
6c2fc1444d ssb: Fix some SQL syntax for private messages. They need more work. 2025-01-29 12:15:46 -05:00
94d969fe46 update: CodeMirror. 2025-01-28 22:23:52 -05:00
a7bc3d301c cleanup: format. 2025-01-28 22:00:48 -05:00
18bab849f7 ssb: prettier. 2025-01-27 21:02:15 -05:00
04878fcc30 ssb: Fix multiple bugs with follow status lying. 2025-01-27 20:28:44 -05:00
ffe1299548 ssb: More room for activities in the emoji picker dialog. 2025-01-27 12:45:54 -05:00
64bdbf5725 build: Never mind. flatpak tools don't want to work in docker. 2025-01-27 12:11:41 -05:00
b82deb557f build: https://github.com/flatpak/flatpak-builder/issues/237 ?? 2025-01-26 21:43:18 -05:00
d529a48a11 build: flatpak maybe? 2025-01-26 21:14:55 -05:00
b711e4e6bd build: Can the CI worker build the flatpak? Let's find out. 2025-01-26 20:47:44 -05:00
186eecfbff build: I don't know why this works better, but it does. 2025-01-26 20:43:02 -05:00
d766c33f59 build: Stop using lto. Not worth the hassle. 2025-01-26 09:56:53 -05:00
2c9257f1a8 build: Trying to understand a build thing. 2025-01-26 09:33:45 -05:00
71ff604f90 ssb: Stop breaking up the news queries into chunks. It sometimes produced incorrect results, and it is not necessary if we're fast enough. 2025-01-25 20:13:05 -05:00
83e00763ea ssb: Be a bit more aggressive about loading latest messages. It bugs me when I don't see my own reactions in a channel or whatnot, and I think it has to do with the queried time ranges. 2025-01-25 18:00:06 -05:00
5b647e0937 buttfeed: Use markdown that works better in Manyverse. 2025-01-25 08:08:02 -05:00
e0444510f4 trace: Use our own sequential thread ids to be more compatible with different trace viewers. 2025-01-24 19:00:40 -05:00
82e876a892 ssb: Only re-populate the messages_stats table on first creation. 2025-01-24 18:06:31 -05:00
c728e05032 ssb: Add a lookup table of max sequence and timestamp per account.f 2025-01-22 19:19:50 -05:00
c7a8ce7060 apps: display: grid. 2025-01-22 19:13:15 -05:00
762b4339fe ssb: Better 'Load More', too. 2025-01-22 18:28:55 -05:00
f824b8988e ssb: Correctness around tunnel.endpoints. 2025-01-22 18:03:05 -05:00
6280d6d167 apps: Mobile CSS tweaks. 2025-01-21 21:46:48 -05:00
4f18e744b4 ssb: Unbreak the channel query. 2025-01-21 21:31:16 -05:00
01d8f720e8 ssb: Treat timestamps of related messages separately from those of the primary queried messages. 2025-01-21 21:07:37 -05:00
18cf058af3 ssb: Experimenting with the news query strategy. 2025-01-21 20:53:23 -05:00
e2406df367 apps: Messing with CSS. 2025-01-20 20:45:35 -05:00
1fd669bdb3 update: npm. 2025-01-20 20:25:31 -05:00
f6add12c80 build: Update the change notes. 2025-01-20 17:57:15 -05:00
0f643bfe39 ssb: Complete using an invite, following the pub and posting a pub message. #99 2025-01-20 17:47:30 -05:00
15be498e4b ssb: Don't show connections in various states of disconnectedness in the sidebar. 2025-01-20 16:57:22 -05:00
fba465dd62 ssb: Allow a concurrent connection if the other one is a connection in the process of closing. 2025-01-20 16:49:21 -05:00
19dbe354e7 core: Consolidate default global setting values in one place. 2025-01-20 14:23:41 -05:00
fca5d37b7e ssb: Add an option to control whether we talk to strangers. #98 2025-01-20 13:35:28 -05:00
aa04ad2dc2 ssb: Clean up used and expired invites. 2025-01-20 08:01:24 -05:00
7ef4d814ef ssb: Avoid showing a broken img when viewing a profile without one assigned. 2025-01-19 21:34:42 -05:00
3f3deb665c ssb: Add a command-line action to generate an invite, and verified that Patchwork can accept it. 2025-01-19 21:00:38 -05:00
97fc22ce57 ssb: Test the one message generated from an invite so far. 2025-01-19 18:38:26 -05:00
616f3ad76d ssb: Invite support progress. Now the pub accepts the invite, tracks its use, and follows. The client still needs to react. 2025-01-19 17:02:08 -05:00
faca63946c build: Fix make docs. 2025-01-19 16:03:21 -05:00
57bae341a2 build: Missed an include. 2025-01-19 16:02:51 -05:00
fd09a766d2 ssb: Work in progress invite support. We can generate them. We can connect using an invite code. We can't yet invite.use(). 2025-01-19 16:00:37 -05:00
11564a5292 core: Let's try some neutral colors to avoid clashing with app colors. 2025-01-18 21:56:43 -05:00
ac12e350bf ssb: People without profile images get emoji faces. I'm amazed it took me this long. 2025-01-18 21:17:07 -05:00
2def15337d update: speedscope 1.22.0. 2025-01-17 17:55:47 -05:00
3e3d58a4a9 ssb: Try harder to avoid doing things with new connections during shutdown. 2025-01-16 12:47:50 -05:00
5ce4f55228 update: speedscope 1.21.2. 2025-01-16 12:35:43 -05:00
21788fc7b0 ssb: Stared at the news feed queries for performance and correctness a bit. 2025-01-15 19:32:31 -05:00
a1c4382fde update: libuv 1.50.0. 2025-01-15 18:53:34 -05:00
364e95698e ssb: Remove the react confirmation dialog. 2025-01-15 12:34:02 -05:00
6eec142499 update: sqlite 3.48.0. 2025-01-15 12:14:46 -05:00
a8f6b3a39a ssb: pub messaged needed padding. 2025-01-14 21:50:38 -05:00
250933bf41 ssb: Suppress noisy output when running command-line actions with output intended to be parsed. 2025-01-14 21:37:11 -05:00
56c77c781a ssb: Placeholder messages needed padding. 2025-01-14 21:18:49 -05:00
f7602b39a1 ssb: The compose preview needed some padding. 2025-01-14 20:51:56 -05:00
8c86092356 ssb: Make the refresh icon a circle. 2025-01-14 20:29:12 -05:00
db0a4bff77 ssb: Use most recent post timestamps to feature more relevant people to follow. 2025-01-14 20:15:18 -05:00
e198ff9cb1 ssb: Show some suggested accounts to follow. 2025-01-12 14:54:09 -05:00
b8eaa5cf97 ssb: prettier. 2025-01-12 12:08:50 -05:00
0d597721bf ssb: Try harder to avoid a full re-render. It's disruptive. 2025-01-12 12:01:52 -05:00
003e0caada ssb: Distant/unknown users get a circle avatar, too. 2025-01-12 11:56:59 -05:00
053637cfb4 ssb: The sync button indicates if a one-shot sync is active. 2025-01-12 11:54:30 -05:00
8178213f1a ssb: Keep the previous db location for android. wip release notes. 2025-01-11 16:14:39 -05:00
b4222a41de update: CodeMirror. 2025-01-11 16:02:52 -05:00
f28e409ea5 ssb: Add a get_contacts command to enumerate follows, blocks, and friends. 2025-01-11 15:49:49 -05:00
287c6c06e1 core: More clean shutdown? 2025-01-11 14:48:12 -05:00
8216bdb4b3 core: Poking at task cleanup. 2025-01-11 14:41:46 -05:00
aa15da50ab ssb: Tried to do the right lit things to prevent unnecessary re-rendering. Ended up doing a lazy JSON thing. 2025-01-11 14:09:42 -05:00
02759c6f83 ssb: Hint at follow depth with profile image shape. Also, reload follow information the same way we re-determine channel unread status. Let's see if this feels good. 2025-01-11 13:48:06 -05:00
6b0c49752c ssb: Trying to learn about occasional errors updating connection info in the database. 2025-01-11 13:02:30 -05:00
2e4f792fc3 ssb: Try to consolidate the local users list. Man, CSS. 2025-01-11 12:48:44 -05:00
17eba059f0 ssb: Fix trying to connect to the same stored connection over and over. 2025-01-11 12:32:18 -05:00
e59a00922b ssb: Respond to tunnel.endpoints. Patchwork didn't seem to like that we responded to tunnel.isRoom but not this. 2025-01-11 09:23:12 -05:00
872201c886 ssb: Support publishing private messages from the command-line. #89 2025-01-08 20:16:17 -05:00
3352098284 ssb: Connections established for a one-shot sync timeout due to inactivity so that the sync eventually completes. 2025-01-07 12:48:21 -05:00
d0bbd7f24f ssb: Add a has_blob command. #89 2025-01-06 20:46:16 -05:00
7f87714b58 ssb: Add some missing padding. 2025-01-05 21:43:26 -05:00
5594bee618 ssb: Add get_identity, get_sequence, and get_profile commands. #89 2025-01-05 17:16:05 -05:00
c469ef23e6 ssb: Make the profile layout a little friendlier. 2025-01-05 15:49:57 -05:00
f6e74f2526 ssb: Try to fix profile layout yet again. Man, CSS. 2025-01-05 15:41:56 -05:00
10b6e9c537 ssb: Remove the weird option to make the server account follow you. Now that this account is admin-controlled, it's unnecessary. 2025-01-05 15:29:29 -05:00
3f27af30b7 ssb: Actually fall back to this default global setting value. 2025-01-05 15:17:41 -05:00
23db09f9b7 core: Default to loading into the ssb app. No more messing around. 2025-01-05 14:52:27 -05:00
d1b7681efc ssb: Don't show 'Loading...' forever in the ssb app when not signed in. Direct to the login page. 2025-01-05 12:52:52 -05:00
61ad405ad8 ssb: Too bright. 2025-01-04 21:38:52 -05:00
aff98110e0 ssb: Tidy up some of the more common reasons for disconnect. 2025-01-04 21:37:43 -05:00
2f36db9142 ssb: Don't display mentions for tags that are used in the text of a message. 2025-01-04 21:05:24 -05:00
aa86ee1066 cleanup: rm test.c 2025-01-04 17:12:00 -05:00
dbbcce8165 ssb: Don't store connections that aren't user-initiated. 2025-01-04 17:08:36 -05:00
1ed066ef0f ssb: Fix naked hashtag links (to the corresponding channel). 2025-01-04 13:03:58 -05:00
763f7d45d8 ssb: Prettier. 2025-01-04 12:41:04 -05:00
2328f3afb5 ssb: Ease up on excessively re-hitting the database for ebt.replicate even more. 2025-01-04 09:58:16 -05:00
2223245861 ssb: Maybe ease up on hammering the db for follows. 2025-01-03 18:17:54 -05:00
36226b01cd ssb: Manage new message handling from the new EBT code. 2025-01-03 17:04:38 -05:00
da31f9cadd cleanup: get/set sent clock is now unused. 2025-01-03 16:56:04 -05:00
9da4857066 ssb: Make the client a bit less aggressive about determining private messages every load. 2025-01-03 15:53:19 -05:00
75c71135ba ssb: No longer replicate every account we hear about. 2025-01-03 15:25:59 -05:00
0cb5025a16 core: Improve global setting grammar. 2025-01-03 14:10:27 -05:00
44d9f69434 ssb: Refactoring EBT implementation. I think this works not worse than before and will let me schedule message replication better in a future change. #93 2025-01-03 13:59:25 -05:00
3f343b283b ssb: Delete one more redundant global setting accessor. 2025-01-03 08:41:13 -05:00
03a28fc3c5 update: CodeMirror. 2025-01-03 08:19:41 -05:00
3513619221 ssb: Reduce message margins. 2025-01-02 21:16:55 -05:00
0c9f5769d3 build: Trying to fix flatpak build for some reason. 2025-01-02 17:45:27 -05:00
587a666ab6 build: Needed out/ earlier for ssl-local. 2025-01-02 17:40:29 -05:00
f26deea508 build: mkdir out/openssl-local. 2025-01-02 17:35:49 -05:00
b8e19040b5 ssb: Fiddling with render of encrypted messages. 2025-01-02 16:11:04 -05:00
7d9e0f4080 macos: Fix build. 2025-01-02 14:20:58 -05:00
16ce7fbc7b ssb: Fix the message encrypted icon placement. 2025-01-02 13:55:47 -05:00
639fce376a ssb: More uv_async_send paranoia still. #96 2025-01-02 13:01:09 -05:00
3cdbac5c22 build: Archive the windows .exe with data. 2025-01-02 13:00:42 -05:00
3dcafdf403 ssb: More uv_async_send paranoia. #96 2025-01-02 12:40:11 -05:00
cd2fe9f8d9 ssb: Fix a crash on Windows when we would call uv_async_send on a handle that had already been closed. Various other cleanup and improvements along the journey. #96 2025-01-02 12:35:58 -05:00
fd40596ce7 ssb: Every now and then I load in Chrome and see everywhere I used overflow: scroll when I wanted overflow: auto. 2025-01-02 08:58:10 -05:00
7ecda69703 ssb: tags never got an updated CSS treatment. 2025-01-02 08:49:30 -05:00
a3b76cd5c2 core: Let's try getting crash callstacks on win32 with a vectored exception handler. 2025-01-02 08:32:18 -05:00
54df862998 ssb: Continuing to untangle message CSS. 2025-01-01 16:44:16 -05:00
301b7a4911 ssb: Trying to untangle some message formatting ugliness. First step: some minor refactoring. 2025-01-01 15:45:11 -05:00
e0a048abe6 follow: This app had never been updated since jsonb, whoops. #94 2025-01-01 15:28:19 -05:00
223 changed files with 23267 additions and 11617 deletions

View File

@@ -1,4 +1,3 @@
.svn
db.sqlite
out/**/*.o
out/**/*.d
.git
db.sqlite*
out/

View File

@@ -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 -j`nproc` 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:
@@ -23,15 +59,13 @@ jobs:
- name: Setup Android SDK
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
packages: 'tools platform-tools build-tools;35.0.0 platforms;android-35 ndk;27.2.12479018'
- name: Docker build
run: DOCKER_BUILDKIT=1 docker build .
- name: Build
run: ANDROID_SDK=$HOME/.android/sdk make -j`nproc` all dist
- name: Upload artifacts
uses: actions/upload-artifact@v3
with:
path: |
out/TildeFriends-release.fdroid.apk
out/winrelease/tildefriends.exe
out/tildefriends-x86_64.AppImage
out/release/tildefriends.standalone
out/armrelease/tildefriends.standalone
name: dist
path: dist/*

4
.gitignore vendored
View File

@@ -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
View File

@@ -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

View File

@@ -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

375
Doxyfile
View File

@@ -1,4 +1,4 @@
# Doxyfile 1.9.8
# Doxyfile 1.9.4
# This file describes the settings to be used by the documentation system
# doxygen (www.doxygen.org) for a project.
@@ -19,8 +19,7 @@
# configuration file:
# doxygen -x [configFile]
# Use doxygen to compare the used configuration file with the template
# configuration file without replacing the environment variables or CMake type
# replacement variables:
# configuration file without replacing the environment variables:
# doxygen -x_noenv [configFile]
#---------------------------------------------------------------------------
@@ -86,7 +85,7 @@ CREATE_SUBDIRS = NO
# level increment doubles the number of directories, resulting in 4096
# directories at level 8 which is the default and also the maximum value. The
# sub-directories are organized in 2 levels, the first level always has a fixed
# number of 16 directories.
# numer of 16 directories.
# Minimum value: 0, maximum value: 8, default value: 8.
# This tag requires that the tag CREATE_SUBDIRS is set to YES.
@@ -342,7 +341,7 @@ OPTIMIZE_OUTPUT_SLICE = NO
#
# Note see also the list of default file extension mappings.
EXTENSION_MAPPING =
EXTENSION_MAPPING = js=javascript
# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
# according to the Markdown format, which allows for more readable
@@ -363,17 +362,6 @@ MARKDOWN_SUPPORT = YES
TOC_INCLUDE_HEADINGS = 5
# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to
# generate identifiers for the Markdown headings. Note: Every identifier is
# unique.
# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a
# sequence number starting at 0 and GITHUB use the lower case version of title
# with any whitespace replaced by '-' and punctuation characters removed.
# The default value is: DOXYGEN.
# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
MARKDOWN_ID_STYLE = DOXYGEN
# When enabled doxygen tries to link words that correspond to documented
# classes, or namespaces to their corresponding documentation. Such a link can
# be prevented in individual cases by putting a % sign in front of the word or
@@ -498,14 +486,6 @@ LOOKUP_CACHE_SIZE = 0
NUM_PROC_THREADS = 1
# If the TIMESTAMP tag is set different from NO then each generated page will
# contain the date or date and time when the page was generated. Setting this to
# NO can help when comparing the output of multiple runs.
# Possible values are: YES, NO, DATETIME and DATE.
# The default value is: NO.
TIMESTAMP = NO
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
@@ -587,8 +567,7 @@ HIDE_UNDOC_MEMBERS = NO
# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
# undocumented classes that are normally visible in the class hierarchy. If set
# to NO, these classes will be included in the various overviews. This option
# will also hide undocumented C++ concepts if enabled. This option has no effect
# if EXTRACT_ALL is enabled.
# has no effect if EXTRACT_ALL is enabled.
# The default value is: NO.
HIDE_UNDOC_CLASSES = NO
@@ -626,8 +605,7 @@ INTERNAL_DOCS = NO
# Windows (including Cygwin) and MacOS, users should typically set this option
# to NO, whereas on Linux or other Unix flavors it should typically be set to
# YES.
# Possible values are: SYSTEM, NO and YES.
# The default value is: SYSTEM.
# The default value is: system dependent.
CASE_SENSE_NAMES = YES
@@ -879,26 +857,11 @@ WARN_IF_INCOMPLETE_DOC = YES
WARN_NO_PARAMDOC = NO
# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about
# undocumented enumeration values. If set to NO, doxygen will accept
# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag
# will automatically be disabled.
# The default value is: NO.
WARN_IF_UNDOC_ENUM_VAL = NO
# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS
# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but
# at the end of the doxygen process doxygen will return with a non-zero status.
# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves
# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not
# write the warning messages in between other messages but write them at the end
# of a run, in case a WARN_LOGFILE is defined the warning messages will be
# besides being in the defined file also be shown at the end of a run, unless
# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case
# the behavior will remain as with the setting FAIL_ON_WARNINGS.
# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT.
# Possible values are: NO, YES and FAIL_ON_WARNINGS.
# The default value is: NO.
WARN_AS_ERROR = NO
@@ -943,28 +906,24 @@ 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 \
core/app.js \
core/client.js \
core/core.js \
core/http.js \
core/tfrpc.js \
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
# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
# documentation (see:
# https://www.gnu.org/software/libiconv/) for the list of possible encodings.
# See also: INPUT_FILE_ENCODING
# The default value is: UTF-8.
INPUT_ENCODING = UTF-8
# This tag can be used to specify the character encoding of the source files
# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify
# character encoding on a per file pattern basis. Doxygen will compare the file
# name with each pattern and apply the encoding instead of the default
# INPUT_ENCODING) if there is a match. The character encodings are a list of the
# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding
# "INPUT_ENCODING" for further information on supported encodings.
INPUT_FILE_ENCODING =
# If the value of the INPUT tag contains directories, you can use the
# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
# *.h) to filter out the source-files in the directories.
@@ -976,14 +935,15 @@ INPUT_FILE_ENCODING =
# Note the list of default checked file patterns might differ from the list of
# default file extension mappings.
#
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm,
# *.cpp, *.cppm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl,
# *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, *.php,
# *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be
# provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08,
# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice.
# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
# *.hh, *.hxx, *.hpp, *.h++, *.l, *.cs, *.d, *.php, *.php4, *.php5, *.phtml,
# *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C
# comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
# *.vhdl, *.ucf, *.qsf and *.ice.
FILE_PATTERNS = *.h \
*.js \
*.md
# The RECURSIVE tag can be used to specify whether or not subdirectories should
@@ -1022,6 +982,9 @@ EXCLUDE_PATTERNS =
# output. The symbol name can be a fully qualified name, a word, or if the
# wildcard * is used, a substring. Examples: ANamespace, AClass,
# ANamespace::AClass, ANamespace::*Test
#
# Note that the wildcards are matched against the file with absolute path, so to
# exclude all test directories use the pattern */test/*
EXCLUDE_SYMBOLS =
@@ -1049,7 +1012,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
@@ -1066,11 +1029,6 @@ IMAGE_PATH =
# code is scanned, but not when the output code is generated. If lines are added
# or removed, the anchors will not be placed correctly.
#
# Note that doxygen will use the data processed and written to standard output
# for further processing, therefore nothing else, like debug statements or used
# commands (so in case of a Windows batch file always use @echo OFF), should be
# written to standard output.
#
# Note that for custom extensions or not directly supported extensions you also
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
@@ -1110,16 +1068,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 =
# The Fortran standard specifies that for fixed formatted Fortran code all
# characters from position 72 are to be considered as comment. A common
# extension is to allow longer lines before the automatic comment starts. The
# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can
# be processed before the automatic comment starts.
# Minimum value: 7, maximum value: 10000, default value: 72.
FORTRAN_COMMENT_AFTER = 72
USE_MDFILE_AS_MAINPAGE = README.md
#---------------------------------------------------------------------------
# Configuration options related to source browsing
@@ -1258,11 +1207,10 @@ CLANG_DATABASE_PATH =
ALPHABETICAL_INDEX = YES
# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes)
# that should be ignored while generating the index headers. The IGNORE_PREFIX
# tag works for classes, function and member names. The entity will be placed in
# the alphabetical list under the first letter of the entity name that remains
# after removing the prefix.
# In case all classes in a project start with a common prefix, all classes will
# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
# can be used to specify a prefix (or a list of prefixes) that should be ignored
# while generating the index headers.
# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
IGNORE_PREFIX =
@@ -1341,12 +1289,7 @@ HTML_STYLESHEET =
# Doxygen will copy the style sheet files to the output directory.
# Note: The order of the extra style sheet files is of importance (e.g. the last
# style sheet in the list overrules the setting of the previous ones in the
# list).
# Note: Since the styling of scrollbars can currently not be overruled in
# Webkit/Chromium, the styling will be left out of the default doxygen.css if
# one or more extra stylesheets have been specified. So if scrollbar
# customization is desired it has to be added explicitly. For an example see the
# documentation.
# list). For an example see the documentation.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_EXTRA_STYLESHEET =
@@ -1361,19 +1304,6 @@ HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output
# should be rendered with a dark or light theme.
# Possible values are: LIGHT always generate light mode output, DARK always
# generate dark mode output, AUTO_LIGHT automatically set the mode according to
# the user preference, use light mode if no preference is set (the default),
# AUTO_DARK automatically set the mode according to the user preference, use
# dark mode if no preference is set and TOGGLE allow to user to switch between
# light and dark mode via a button.
# The default value is: AUTO_LIGHT.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_COLORSTYLE = AUTO_LIGHT
# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
# will adjust the colors in the style sheet and background images according to
# this color. Hue is specified as an angle on a color-wheel, see
@@ -1404,6 +1334,15 @@ HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
# page will contain the date and time when the page was generated. Setting this
# to YES can help to show when doxygen was last run and thus if the
# documentation is up to date.
# The default value is: NO.
# This tag requires that the tag GENERATE_HTML is set to YES.
#HTML_TIMESTAMP = NO
# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
# documentation will contain a main index with vertical navigation menus that
# are dynamically created via JavaScript. If disabled, the navigation index will
@@ -1423,13 +1362,6 @@ HTML_DYNAMIC_MENUS = YES
HTML_DYNAMIC_SECTIONS = NO
# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be
# dynamically folded and expanded in the generated HTML source code.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
HTML_CODE_FOLDING = YES
# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
# shown in the various tree structured indices initially; the user can expand
# and collapse entries dynamically later on. Doxygen will expand the tree to
@@ -1560,16 +1492,6 @@ BINARY_TOC = NO
TOC_EXPAND = NO
# The SITEMAP_URL tag is used to specify the full URL of the place where the
# generated documentation will be placed on the server by the user during the
# deployment of the documentation. The generated sitemap is called sitemap.xml
# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL
# is specified no sitemap is generated. For information about the sitemap
# protocol see https://www.sitemaps.org
# This tag requires that the tag GENERATE_HTML is set to YES.
SITEMAP_URL =
# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
@@ -1680,7 +1602,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
@@ -1745,6 +1667,17 @@ HTML_FORMULA_FORMAT = png
FORMULA_FONTSIZE = 10
# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
# generated for formulas are transparent PNGs. Transparent PNGs are not
# supported properly for IE 6.0, but are supported on all modern browsers.
#
# Note that when changing this option you need to delete any form_*.png files in
# the HTML output directory before the changes have effect.
# The default value is: YES.
# This tag requires that the tag GENERATE_HTML is set to YES.
#FORMULA_TRANSPARENT = YES
# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
# to create new LaTeX commands to be used in formulas as building blocks. See
# the section "Including formulas" for details.
@@ -1806,8 +1739,8 @@ MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2
# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
# extension names that should be enabled during MathJax rendering. For example
# for MathJax version 2 (see
# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions):
# for MathJax version 2 (see https://docs.mathjax.org/en/v2.7-latest/tex.html
# #tex-and-latex-extensions):
# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
# For example for MathJax version 3 (see
# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html):
@@ -2058,16 +1991,9 @@ PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
# The LATEX_BATCHMODE tag signals the behavior of LaTeX in case of an error.
# Possible values are: NO same as ERROR_STOP, YES same as BATCH, BATCH In batch
# mode nothing is printed on the terminal, errors are scrolled as if <return> is
# hit at every error; missing files that TeX tries to input or request from
# keyboard input (\read on a not open input stream) cause the job to abort,
# NON_STOP In nonstop mode the diagnostic message will appear on the terminal,
# but there is no possibility of user interaction just like in batch mode,
# SCROLL In scroll mode, TeX will stop only for missing files to input or if
# keyboard input is necessary and ERROR_STOP In errorstop mode, TeX will stop at
# each error, asking for user intervention.
# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
# command to the generated LaTeX files. This will instruct LaTeX to keep running
# if errors occur, instead of asking the user for help.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
@@ -2088,6 +2014,14 @@ LATEX_HIDE_INDICES = NO
LATEX_BIB_STYLE = plain
# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
# page will contain the date and time when the page was generated. Setting this
# to NO can help when comparing the output of multiple runs.
# The default value is: NO.
# This tag requires that the tag GENERATE_LATEX is set to YES.
#LATEX_TIMESTAMP = NO
# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
# path from which the emoji images will be read. If a relative path is entered,
# it will be relative to the LATEX_OUTPUT directory. If left blank the
@@ -2253,39 +2187,13 @@ DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
# AutoGen Definitions (see https://autogen.sourceforge.net/) file that captures
# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
# the structure of the code including all documentation. Note that this feature
# is still experimental and incomplete at the moment.
# The default value is: NO.
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# Configuration options related to Sqlite3 output
#---------------------------------------------------------------------------
# If the GENERATE_SQLITE3 tag is set to YES doxygen will generate a Sqlite3
# database with symbols found by doxygen stored in tables.
# The default value is: 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
# in front of it.
# The default directory is: sqlite3.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
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
# will warn if an a database file is already found and not modify it.
# The default value is: YES.
# This tag requires that the tag GENERATE_SQLITE3 is set to YES.
SQLITE3_RECREATE_DB = YES
#---------------------------------------------------------------------------
# Configuration options related to the Perl module output
#---------------------------------------------------------------------------
@@ -2428,15 +2336,15 @@ TAGFILES =
GENERATE_TAGFILE =
# If the ALLEXTERNALS tag is set to YES, all external classes and namespaces
# will be listed in the class and namespace index. If set to NO, only the
# inherited external classes will be listed.
# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
# the class index. If set to NO, only the inherited external classes will be
# listed.
# The default value is: NO.
ALLEXTERNALS = NO
# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
# in the topic index. If set to NO, only the current project's groups will be
# in the modules index. If set to NO, only the current project's groups will be
# listed.
# The default value is: YES.
@@ -2450,9 +2358,16 @@ EXTERNAL_GROUPS = YES
EXTERNAL_PAGES = YES
#---------------------------------------------------------------------------
# Configuration options related to diagram generator tools
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
DIA_PATH =
# If set to YES the inheritance and collaboration graphs will hide inheritance
# and usage relations if the target is undocumented or is not a class.
# The default value is: YES.
@@ -2461,10 +2376,10 @@ HIDE_UNDOC_RELATIONS = YES
# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
# available from the path. This tool is part of Graphviz (see:
# https://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
# Bell Labs. The other options in this section have no effect if this option is
# set to NO
# The default value is: YES.
# The default value is: NO.
HAVE_DOT = YES
@@ -2478,51 +2393,37 @@ HAVE_DOT = YES
DOT_NUM_THREADS = 0
# DOT_COMMON_ATTR is common attributes for nodes, edges and labels of
# subgraphs. When you want a differently looking font in the dot files that
# doxygen generates you can specify fontname, fontcolor and fontsize attributes.
# For details please see <a href=https://graphviz.org/doc/info/attrs.html>Node,
# Edge and Graph Attributes specification</a> You need to make sure dot is able
# to find the font, which can be done by putting it in a standard location or by
# setting the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the
# directory containing the font. Default graphviz fontsize is 14.
# The default value is: fontname=Helvetica,fontsize=10.
# When you want a differently looking font in the dot files that doxygen
# generates you can specify the font name using DOT_FONTNAME. You need to make
# sure dot is able to find the font, which can be done by putting it in a
# standard location or by setting the DOTFONTPATH environment variable or by
# setting DOT_FONTPATH to the directory containing the font.
# The default value is: Helvetica.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_COMMON_ATTR = "fontname=Helvetica,fontsize=10"
#DOT_FONTNAME = Helvetica
# DOT_EDGE_ATTR is concatenated with DOT_COMMON_ATTR. For elegant style you can
# add 'arrowhead=open, arrowtail=open, arrowsize=0.5'. <a
# href=https://graphviz.org/doc/info/arrows.html>Complete documentation about
# arrows shapes.</a>
# The default value is: labelfontname=Helvetica,labelfontsize=10.
# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
# dot graphs.
# Minimum value: 4, maximum value: 24, default value: 10.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_EDGE_ATTR = "labelfontname=Helvetica,labelfontsize=10"
#DOT_FONTSIZE = 10
# DOT_NODE_ATTR is concatenated with DOT_COMMON_ATTR. For view without boxes
# around nodes set 'shape=plain' or 'shape=plaintext' <a
# href=https://www.graphviz.org/doc/info/shapes.html>Shapes specification</a>
# The default value is: shape=box,height=0.2,width=0.4.
# By default doxygen will tell dot to use the default font as specified with
# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
# the path where dot can find it using this tag.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_NODE_ATTR = "shape=box,height=0.2,width=0.4"
#DOT_FONTPATH =
# You can set the path where dot can find font specified with fontname in
# DOT_COMMON_ATTR and others dot attributes.
# This tag requires that the tag HAVE_DOT is set to YES.
DOT_FONTPATH =
# If the CLASS_GRAPH tag is set to YES or GRAPH or BUILTIN then doxygen will
# generate a graph for each documented class showing the direct and indirect
# inheritance relations. In case the CLASS_GRAPH tag is set to YES or GRAPH and
# HAVE_DOT is enabled as well, then dot will be used to draw the graph. In case
# the CLASS_GRAPH tag is set to YES and HAVE_DOT is disabled or if the
# CLASS_GRAPH tag is set to BUILTIN, then the built-in generator will be used.
# If the CLASS_GRAPH tag is set to TEXT the direct and indirect inheritance
# relations will be shown as texts / links.
# Possible values are: NO, YES, TEXT, GRAPH and BUILTIN.
# If the CLASS_GRAPH tag is set to YES (or GRAPH) then doxygen will generate a
# graph for each documented class showing the direct and indirect inheritance
# relations. In case HAVE_DOT is set as well dot will be used to draw the graph,
# otherwise the built-in generator will be used. If the CLASS_GRAPH tag is set
# to TEXT the direct and indirect inheritance relations will be shown as texts /
# links.
# Possible values are: NO, YES, TEXT and GRAPH.
# The default value is: YES.
CLASS_GRAPH = YES
@@ -2530,21 +2431,15 @@ CLASS_GRAPH = YES
# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
# graph for each documented class showing the direct and indirect implementation
# dependencies (inheritance, containment, and class references variables) of the
# class with other documented classes. Explicit enabling a collaboration graph,
# when COLLABORATION_GRAPH is set to NO, can be accomplished by means of the
# command \collaborationgraph. Disabling a collaboration graph can be
# accomplished by means of the command \hidecollaborationgraph.
# class with other documented classes.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
COLLABORATION_GRAPH = YES
# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
# groups, showing the direct groups dependencies. Explicit enabling a group
# dependency graph, when GROUP_GRAPHS is set to NO, can be accomplished by means
# of the command \groupgraph. Disabling a directory graph can be accomplished by
# means of the command \hidegroupgraph. See also the chapter Grouping in the
# manual.
# groups, showing the direct groups dependencies. See also the chapter Grouping
# in the manual.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2604,9 +2499,7 @@ TEMPLATE_RELATIONS = NO
# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
# YES then doxygen will generate a graph for each documented file showing the
# direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an include graph, when INCLUDE_GRAPH is is set to NO,
# can be accomplished by means of the command \includegraph. Disabling an
# include graph can be accomplished by means of the command \hideincludegraph.
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2615,10 +2508,7 @@ INCLUDE_GRAPH = YES
# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
# set to YES then doxygen will generate a graph for each documented file showing
# the direct and indirect include dependencies of the file with other documented
# files. Explicit enabling an included by graph, when INCLUDED_BY_GRAPH is set
# to NO, can be accomplished by means of the command \includedbygraph. Disabling
# an included by graph can be accomplished by means of the command
# \hideincludedbygraph.
# files.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2658,10 +2548,7 @@ GRAPHICAL_HIERARCHY = YES
# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
# dependencies a directory has on other directories in a graphical way. The
# dependency relations are determined by the #include relations between the
# files in the directories. Explicit enabling a directory graph, when
# DIRECTORY_GRAPH is set to NO, can be accomplished by means of the command
# \directorygraph. Disabling a directory graph can be accomplished by means of
# the command \hidedirectorygraph.
# files in the directories.
# The default value is: YES.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2677,13 +2564,12 @@ DIR_GRAPH_MAX_DEPTH = 1
# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
# generated by dot. For an explanation of the image formats see the section
# output formats in the documentation of the dot tool (Graphviz (see:
# https://www.graphviz.org/)).
# http://www.graphviz.org/)).
# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
# to make the SVG files visible in IE 9+ (other browsers do not have this
# requirement).
# Possible values are: png, jpg, jpg:cairo, jpg:cairo:gd, jpg:gd, jpg:gd:gd,
# gif, gif:cairo, gif:cairo:gd, gif:gd, gif:gd:gd, svg, png:gd, png:gd:gd,
# png:cairo, png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
# png:gdiplus:gdiplus.
# The default value is: png.
# This tag requires that the tag HAVE_DOT is set to YES.
@@ -2715,12 +2601,11 @@ DOT_PATH =
DOTFILE_DIRS =
# You can include diagrams made with dia in doxygen documentation. Doxygen will
# then run dia to produce the diagram and insert it in the documentation. The
# DIA_PATH tag allows you to specify the directory where the dia binary resides.
# If left empty dia is assumed to be found in the default search path.
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
DIA_PATH =
MSCFILE_DIRS =
# The DIAFILE_DIRS tag can be used to specify one or more directories that
# contain dia files that are included in the documentation (see the \diafile
@@ -2770,6 +2655,18 @@ DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
# background. This is disabled by default, because dot on Windows does not seem
# to support this out of the box.
#
# Warning: Depending on the platform used, enabling this option may lead to
# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
# read).
# The default value is: NO.
# This tag requires that the tag HAVE_DOT is set to YES.
#DOT_TRANSPARENT = NO
# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
# files in one run (i.e. multiple -o and -T options on the command line). This
# makes dot run faster, but since only newer versions of dot (>1.8.10) support
@@ -2797,19 +2694,3 @@ GENERATE_LEGEND = YES
# The default value is: YES.
DOT_CLEANUP = YES
# You can define message sequence charts within doxygen comments using the \msc
# command. If the MSCGEN_TOOL tag is left empty (the default), then doxygen will
# use a built-in version of mscgen tool to produce the charts. Alternatively,
# the MSCGEN_TOOL tag can also specify the name an external tool. For instance,
# specifying prog as the value, doxygen will call the tool as prog -T
# <outfile_format> -o <outputfile> <inputfile>. The external tool should support
# output file formats "png", "eps", "svg", and "ismap".
MSCGEN_TOOL =
# The MSCFILE_DIRS tag can be used to specify one or more directories that
# contain msc files that are included in the documentation (see the \mscfile
# command).
MSCFILE_DIRS =

View File

@@ -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-wip
VERSION_CODE := 43
VERSION_CODE_IOS := 17
VERSION_NUMBER := 0.2025.9-wip
VERSION_NAME := This program kills fascists.
SQLITE_URL := https://www.sqlite.org/2024/sqlite-amalgamation-3470200.zip
IPHONEOS_VERSION_MIN=14.0
SQLITE_URL := https://www.sqlite.org/2025/sqlite-amalgamation-3500400.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 \
@@ -83,14 +103,13 @@ CFLAGS += \
-g
LDFLAGS += \
-Wno-attributes \
-Wno-aggressive-loop-optimizations \
-flto=auto
-Wno-aggressive-loop-optimizations
ANDROID_MIN_SDK_VERSION := 24
ANDROID_TARGET_SDK_VERSION := 34
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/34.0.0
ANDROID_TARGET_SDK_VERSION := 35
ANDROID_BUILD_TOOLS := $(ANDROID_SDK)/build-tools/35.0.0
ANDROID_PLATFORM := $(ANDROID_SDK)/platforms/android-$(ANDROID_TARGET_SDK_VERSION)
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/26.3.11579264
ANDROID_NDK ?= $(ANDROID_SDK)/ndk/27.2.12479018
ANDROID_ARMV7A_TARGETS := \
out/androiddebug-armv7a/tildefriends \
@@ -127,6 +146,7 @@ WINDOWS_TARGETS := \
out/winrelease/tildefriends.exe
ifeq ($(HAVE_WIN),1)
BUILD_TYPES += windebug winrelease
all: out/windebug/tildefriends.standalone.exe out/winrelease/tildefriends.standalone.exe
endif
AARCH64_TARGETS := \
@@ -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 \
@@ -206,12 +253,14 @@ $(ANDROID_TARGETS): CFLAGS += \
-fno-asynchronous-unwind-tables \
-funwind-tables \
-Wno-unknown-warning-option
$(ANDROID_TARGETS): LDFLAGS += --sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIC
$(ANDROID_TARGETS): LDFLAGS += \
--sysroot $(ANDROID_NDK)/toolchains/llvm/prebuilt/linux-x86_64/sysroot \
-Wl,-z,max-page-size=16384 \
-fPIC
$(DEBUG_TARGETS): CFLAGS += -DDEBUG -Og
$(DEBUG_TARGETS): LDFLAGS += -Og
$(RELEASE_TARGETS): CFLAGS += \
-DNDEBUG \
-flto
-DNDEBUG
$(ANDROID_RELEASE_TARGETS): CFLAGS += -Oz
$(ANDROID_RELEASE_TARGETS): LDFLAGS += -Oz
$(NONANDROID_RELEASE_TARGETS): CFLAGS += -Os
@@ -222,25 +271,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
@@ -251,30 +304,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)
@@ -293,13 +355,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)
@@ -321,10 +384,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 \
@@ -552,16 +617,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
$(UV_OBJS): CFLAGS += -fno-lto
-Wno-unused-variable
$(filter out/win%,$(UV_OBJS)): \
CFLAGS += \
-Wno-cast-function-type \
@@ -588,6 +653,7 @@ SODIUM_SOURCES := \
deps/libsodium/src/libsodium/crypto_core/hsalsa20/ref2/core_hsalsa20_ref2.c \
deps/libsodium/src/libsodium/crypto_core/salsa/ref/core_salsa_ref.c \
deps/libsodium/src/libsodium/crypto_core/softaes/softaes.c \
deps/libsodium/src/libsodium/crypto_generichash/crypto_generichash.c \
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-compress-ref.c \
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/blake2b-ref.c \
deps/libsodium/src/libsodium/crypto_generichash/blake2b/ref/generichash_blake2b.c \
@@ -657,12 +723,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 \
@@ -681,7 +747,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
@@ -758,12 +824,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
@@ -796,27 +862,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) \
@@ -872,6 +926,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:
##
@@ -889,29 +954,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/
@@ -928,11 +994,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 '.*')))
@@ -953,7 +1020,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 \
@@ -962,6 +1029,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
@@ -972,14 +1040,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/
@@ -988,7 +1053,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) --alignment-preserved $@
aab: out/TildeFriends.aab ## Build an Android App Bundle.
.PHONY: aab
@@ -1050,12 +1120,12 @@ out/apk/TildeFriends-%.fdroid.unsigned.apk:
out/%.apk: out/apk/%.unsigned.apk
@echo "[apksigner] $(notdir $@)"
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $<
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $<
out/%.zopfli.apk: out/%.apk
@echo "[zopfli] $(notdir $@)"
$(ANDROID_BUILD_TOOLS)/zipalign -f -z 4 $< $@.zopfli
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ $@.zopfli
@$(ANDROID_BUILD_TOOLS)/apksigner sign --ks .keys/android.jks --ks-key-alias androidKey --ks-pass pass:android --key-pass pass:android --min-sdk-version $(ANDROID_MIN_SDK_VERSION) --out $@ --alignment-preserved $@.zopfli
release-apk: out/TildeFriends-arm-release.zopfli.apk out/TildeFriends-x86-release.zopfli.apk ## Build an Android release APK.
.PHONY: release-apk
@@ -1073,6 +1143,11 @@ releaseapkgo: out/TildeFriends-arm-release.apk ## Build, install, and run a rele
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
.PHONY: releaseapkgo
x86releaseapkgo: out/TildeFriends-x86-release.apk ## Build, install, and run an x86 release Android APK.
@adb install -r $<
@adb shell am start com.unprompted.tildefriends/.TildeFriendsActivity
.PHONY: x86releaseapkgo
apklog: ## Display Android log output.
@adb logcat *:S tildefriends
.PHONY: apklog
@@ -1091,14 +1166,27 @@ out/%.app/tildefriends.png: src/ios/tildefriends.png
@cp -v $< $@
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
@@ -1136,26 +1224,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):
+@OPTIONS=-flto 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- -flto" 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 \
@@ -1164,33 +1260,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):
+@OPTIONS=-flto tools/ssl-local
$(filter $(BUILD_DIR)/macosdebug/%,$(APP_OBJS)) $(filter $(BUILD_DIR)/macosrelease/%,$(APP_OBJS)): | $(LOCAL_DEPS)
+@tools/ssl-local
$(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:
##
@@ -1218,7 +1344,8 @@ appimage: out/tildefriends-x86_64.AppImage ## Build an AppImage.
.PHONY: appimage
flatpak: out/ ## Build a flatpak.
flatpak-builder --force-clean --user --install-deps-from=flathub --install --repo=out/flatpak-repo out/flatpak src/com.unprompted.tildefriends.yml
flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
flatpak-builder --user --disable-rofiles-fuse --install-deps-from=flathub --install --repo=out/flatpak-repo out/flatpak src/com.unprompted.tildefriends.yml
flatpak build-bundle out/flatpak-repo out/tildefriends.flatpak com.unprompted.tildefriends
.PHONY: flatpak
@@ -1258,7 +1385,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 +1395,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 +1416,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 +1441,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 +1492,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

View File

@@ -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

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🎛",
"previous": "&R49FywYF8CXPhoSEydLbSCgvCddeyTiBwGuDU/gqY+M=.sha256"
"previous": "&kmKNyb/uaXNb24gCinJtfS8iWx4cLUWdtl0y2DwEUas=.sha256"
}

View File

@@ -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>

View File

@@ -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}

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "💻",
"previous": "&icsPplXHgmpkbNWyo/WTvRdT/A8BXxW4lJixOtP4ueQ=.sha256"
"previous": "&sFRTDn/RpxP1NJeECXHrXKwCRUJsEOEDVaCMPl50zpM=.sha256"
}

View File

@@ -19,10 +19,6 @@ async function fetch_info(apps) {
return result;
}
/**
*
*
*/
async function fetch_shared_apps() {
let messages = {};
@@ -69,17 +65,17 @@ async function main() {
const stylesheet = `
body {
color: whitesmoke;
font-family: sans-serif;
margin: 16px;
margin: 8px;
}
.container {
.iconbox {
display: grid;
grid-template-columns: repeat(auto-fill, 64px);
gap: 1em;
justify-content: space-around;
background-color: #ffffff10;
border: 2px solid #073642;
border-radius: 8px;
grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
}
.iconbox::after {
content: "";
flex: auto;
}
.app {
@@ -101,16 +97,28 @@ async function main() {
`;
const body = `
<h1 style="text-shadow: #808080 0 0 10px;">Welcome to Tilde Friends.</h1>
<h1>Welcome to Tilde Friends</h1>
<h2>your apps</h2>
<div id="apps" class="container"></div>
<div class="w3-card-4 w3-margin-top">
<header class="w3-container w3-light-blue">
<h2>Your Apps</h2>
</header>
<div id="apps" class="w3-indigo iconbox"></div>
</div>
<h2>shared apps</h2>
<div id="shared_apps" class="container"></div>
<div class="w3-card-4 w3-margin-top">
<header class="w3-container w3-light-blue">
<h2>Shared Apps</h2>
</header>
<div id="shared_apps" class="w3-indigo iconbox"></div>
</div>
<h2>core apps</h2>
<div id="core_apps" class="container"></div>
<div class="w3-card-4 w3-margin-top">
<header class="w3-container w3-light-blue">
<h2>Core Apps</h2>
</header>
<div id="core_apps" class="w3-indigo iconbox"></div>
</div>
`;
const script = `
@@ -126,9 +134,13 @@ async function main() {
// For each app in the provided list
for (let app of Object.keys(apps).sort()) {
// Create the item
let div = list.appendChild(document.createElement('div'));
let inline = document.createElement('div');
inline.style.display = 'inline-block';
inline.classList.add('w3-button');
list.appendChild(inline);
let div = document.createElement('div');
inline.appendChild(div);
div.classList.add('app');
// The app's icon
@@ -161,12 +173,13 @@ async function main() {
<!DOCTYPE html>
<html>
<head>
<link type="text/css" rel="stylesheet" href="w3.css"></link>
<style>
${stylesheet}
</style>
</head>
<body>
<body class="w3-darkgray">
${body}
</body>

251
apps/apps/w3.css Normal file
View 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}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "➡️"
"emoji": "➡️",
"previous": "&YDDSzbRD8NFAykYlZnk4r4hAK5qXjT5LmKE6rhS1s+A=.sha256"
}

View File

@@ -14,7 +14,7 @@ async function contacts_internal(id, last_row_id, following, max_row_id) {
result.blocking = result.blocking || {};
let contacts = await query(
`
SELECT content FROM messages
SELECT json(content) AS content FROM messages
WHERE author = ? AND
rowid > ? AND
rowid <= ? AND
@@ -189,50 +189,6 @@ async function fetch_about(db, ids, users) {
return Object.assign({}, users);
}
async function getAbout(db, id) {
if (g_about_cache[id]) {
return g_about_cache[id];
}
let o = await db.get(id + ':about');
const k_version = 4;
let f = o ? JSON.parse(o) : o;
if (!f || f.version != k_version) {
f = {about: {}, sequence: 0, version: k_version};
}
await ssb.sqlAsync(
'SELECT ' +
' sequence, ' +
' content ' +
'FROM messages ' +
'WHERE ' +
' author = ?1 AND ' +
' sequence > ?2 AND ' +
" json_extract(content, '$.type') = 'about' AND " +
" json_extract(content, '$.about') = ?1 " +
'UNION SELECT MAX(sequence) as sequence, NULL FROM messages WHERE author = ?1 ' +
'ORDER BY sequence',
[id, f.sequence],
function (row) {
f.sequence = row.sequence;
if (row.content) {
let about = {};
try {
about = JSON.parse(row.content);
} catch {}
delete about.about;
delete about.type;
f.about = Object.assign(f.about, about);
}
}
);
let j = JSON.stringify(f);
if (o != j) {
await db.set(id + ':about', j);
}
g_about_cache[id] = f.about;
return f.about;
}
async function getSize(db, id) {
let size = 0;
await ssb.sqlAsync(

View File

@@ -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
View File

@@ -0,0 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "💡",
"previous": "&eN6DNPpQUNhGvxneLuLPgsOXR6qyFZ7u+MAz0b4fa7k=.sha256"
}

16
apps/intro/app.js Normal file
View 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
View 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> -&gt;
<b>Core Apps</b> -&gt; <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">
&#10094;
</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">
&#10095;
</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
View 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}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🚪",
"previous": "&HXCdDG8gGYXElTyEFbg85jqa6lDXNL2ENPIA9UoJNbI=.sha256"
"previous": "&DJwkqNfYWtW9yBtJQMseEXm7l04Enpi+yAxZulLq9Vk=.sha256"
}

View File

@@ -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>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🦀",
"previous": "&bBoMW+AjErDfa483Mg3+h1L25xfDDeVSpcfD9WAwL3U=.sha256"
"previous": "&Hd6CuhhnZIf13PdFJYZBUYLYZO84WdaKvWXLC29M7Ac=.sha256"
}

View File

@@ -21,9 +21,6 @@ tfrpc.register(async function createIdentity() {
tfrpc.register(async function getServerIdentity() {
return ssb.getServerIdentity();
});
tfrpc.register(async function setServerFollowingMe(id, following) {
return ssb.setServerFollowingMe(id, following);
});
tfrpc.register(async function getIdentities() {
return ssb.getIdentities();
});
@@ -106,6 +103,19 @@ tfrpc.register(async function getActiveIdentity() {
tfrpc.register(async function sync() {
return await ssb.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());
});

View File

@@ -7,7 +7,7 @@ function textNode(text) {
function linkNode(text, link) {
const linkNode = new commonmark.Node('link', undefined);
if (link.startsWith('#')) {
linkNode.destination = `#${encodeURIComponent('#' + link)}`;
linkNode.destination = `#${encodeURIComponent(link)}`;
} else {
linkNode.destination = link;
}

View File

@@ -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';
@@ -162,7 +147,7 @@ export async function picker(callback, anchor, author) {
<div class="w3-modal-content w3-card-4">
<div
class="w3-content w3-theme-d1"
style="display: flex; flex-direction: column; max-height: 50vh"
style="display: flex; flex-direction: column; max-height: 80vh"
>
<header class="w3-container" style="flex: 0 0">
<h1>Choose a Reaction</h1>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -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},
@@ -18,6 +19,15 @@ class TfElement extends LitElement {
channels: {type: Array},
channels_unread: {type: Object},
channels_latest: {type: Object},
guest: {type: Boolean},
url: {type: String},
private_closed: {type: Object},
private_messages: {type: Array},
grouped_private_messages: {type: Object},
recent_reactions: {type: Array},
is_administrator: {type: Boolean},
stay_connected: {type: Boolean},
progress: {type: Number},
};
}
@@ -33,11 +43,14 @@ 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_channels_latest = 0;
this.loading_channels_latest_scheduled = 0;
this.loading_latest = 0;
this.loading_latest_scheduled = 0;
this.recent_reactions = [];
this.private_closed = {};
tfrpc.rpc.getBroadcasts().then((b) => {
self.broadcasts = b || [];
});
@@ -47,6 +60,7 @@ class TfElement extends LitElement {
tfrpc.rpc.getHash().then((hash) => self.set_hash(hash));
tfrpc.register(function hashChanged(hash) {
self.set_hash(hash);
self.reset_progress();
});
tfrpc.register(async function notifyNewMessage(id) {
await self.fetch_new_message(id);
@@ -66,11 +80,30 @@ class TfElement extends LitElement {
async initial_load() {
let whoami = await tfrpc.rpc.getActiveIdentity();
let ids = (await tfrpc.rpc.getIdentities()) || [];
this.is_administrator = await tfrpc.rpc.isAdministrator();
this.stay_connected =
this.is_administrator &&
(await tfrpc.rpc.globalSettingsGet('stay_connected'));
this.url = await tfrpc.rpc.url();
this.whoami = whoami ?? (ids.length ? ids[0] : undefined);
this.guest = !this.whoami?.length;
this.ids = ids;
let private_closed =
(await tfrpc.rpc.databaseGet('private_closed')) ?? '{}';
this.private_closed = JSON.parse(private_closed);
await this.load_channels();
}
async close_private_chat(event) {
let update = {};
update[event.detail.key] = true;
this.private_closed = Object.assign(update, this.private_closed);
await tfrpc.rpc.databaseSet(
'private_closed',
JSON.stringify(this.private_closed)
);
}
async load_channels() {
let channels = await tfrpc.rpc.query(
`
@@ -118,8 +151,34 @@ class TfElement extends LitElement {
}
}
visible_private() {
if (!this.grouped_private_messages || !this.private_closed) {
return [];
}
let self = this;
return Object.fromEntries(
Object.entries(this.grouped_private_messages).filter(([key, value]) => {
let channel = '🔐' + [...new Set(JSON.parse(key))].sort().join(',');
let grouped_latest = Math.max(...value.map((x) => x.rowid));
return (
!self.private_closed[key] ||
self.channels_unread[channel] === undefined ||
grouped_latest > self.channels_unread[channel]
);
})
);
}
next_channel(delta) {
let channel_names = ['', '@', '🔐', ...this.channels.map((x) => '#' + x)];
let channel_names = [
'',
'@',
'👍',
...Object.keys(this.visible_private())
.sort()
.map((x) => '🔐' + JSON.parse(x).join(',')),
...this.channels.map((x) => '#' + x),
];
let index = channel_names.indexOf(this.hash.substring(1));
index = index != -1 ? index + delta : 0;
tfrpc.rpc.setHash(
@@ -143,8 +202,10 @@ class TfElement extends LitElement {
}
}
async fetch_about(ids, users) {
const k_cache_version = 1;
async fetch_about(following, users) {
this.loading_about++;
let ids = Object.keys(following).sort();
const k_cache_version = 3;
let cache = await tfrpc.rpc.databaseGet('about');
let original_cache = cache;
cache = cache ? JSON.parse(cache) : {};
@@ -152,77 +213,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.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
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.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
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(users[id] || {}, cache.about[id]);
}
return Object.assign({}, users);
}
@@ -244,7 +314,7 @@ class TfElement extends LitElement {
this.load_channels();
}
}
this.schedule_load_channels_latest();
this.schedule_load_latest();
}
async _handle_whoami_changed(event) {
@@ -259,144 +329,321 @@ class TfElement extends LitElement {
}
}
async create_identity() {
if (confirm('Are you sure you want to create a new identity?')) {
await tfrpc.rpc.createIdentity();
this.ids = (await tfrpc.rpc.getIdentities()) || [];
if (this.ids && !this.whoami) {
this.whoami = this.ids[0];
}
}
}
async get_latest_private(following) {
const k_version = 1;
// { "version": 1, "range": [1234, 5678], messages: [ "%1.sha256", "%2.sha256", ... ], latest: rowid }
let cache = JSON.parse(
(await tfrpc.rpc.databaseGet(`private:${this.whoami}`)) ?? '{}'
);
if (cache.version !== k_version) {
cache = {
version: k_version,
messages: [],
range: [],
};
}
let latest = (
await tfrpc.rpc.query('SELECT MAX(rowid) AS latest FROM messages')
)[0].latest;
const k_chunk_count = 256;
while (latest - k_chunk_count >= 0) {
let ranges = [];
const k_chunk_size = 512;
if (cache.range.length) {
for (let i = cache.range[1]; i < latest; i += k_chunk_size) {
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), i, false]);
}
} else {
for (let i = 0; i < latest; i += k_chunk_size) {
ranges.push([i, Math.min(i + k_chunk_size, latest), true]);
}
}
for (let range of ranges) {
let messages = await tfrpc.rpc.query(
`
SELECT messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
SELECT messages.rowid, messages.id, json(content) AS content
FROM messages
JOIN json_each(?1) AS following ON messages.author = following.value
WHERE
messages.rowid > ?2 AND
messages.rowid <= ?3 AND
messages.rowid > ?1 AND
messages.rowid <= ?2 AND
json(messages.content) LIKE '"%'
ORDER BY sequence DESC
ORDER BY messages.rowid DESC
`,
[JSON.stringify(following), latest - k_chunk_count, latest]
[range[0], range[1]]
);
messages = (await this.decrypt(messages)).filter((x) => x.decrypted);
if (messages.length) {
return Math.max(...messages.map((x) => x.rowid));
cache.latest = Math.max(
cache.latest ?? 0,
...messages.map((x) => x.rowid)
);
if (range[2]) {
cache.messages = [...cache.messages, ...messages.map((x) => x.id)];
} else {
cache.messages = [...messages.map((x) => x.id), ...cache.messages];
}
}
latest -= k_chunk_count;
cache.range[0] = Math.min(cache.range[0] ?? range[0], range[0]);
cache.range[1] = Math.max(cache.range[1] ?? range[1], range[1]);
await tfrpc.rpc.databaseSet(
`private:${this.whoami}`,
JSON.stringify(cache)
);
}
return -1;
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.replaceAll(/\s+/g, ' ').trim());
return result;
}
async group_private_messages(messages) {
let groups = {};
let result = await this.decrypt(
await tfrpc.rpc.query(
`
SELECT messages.rowid, messages.id, author, timestamp, json(content) AS content
FROM messages
JOIN json_each(?) AS ids
WHERE messages.id = ids.value
ORDER BY timestamp DESC
`,
[JSON.stringify(messages)]
)
);
for (let message of result) {
let key = JSON.stringify(
[
...new Set(
message?.decrypted?.recps?.filter((x) => x != this.whoami)
),
].sort() ?? []
);
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(message);
}
return groups;
}
async load_channels_latest(following) {
this.loading_channels_latest++;
try {
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])
);
console.log('latest', this.channels_latest);
console.log('unread', this.channels_unread);
console.log('channels took', (new Date() - start_time) / 1000.0);
let self = this;
latest_private.then(function (latest) {
self.channels_latest = Object.assign({}, self.channels_latest, {
'🔐': latest,
});
console.log('private took', (new Date() - start_time) / 1000.0);
let start_time = new Date();
let latest_private = this.get_latest_private(following);
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
),
])
).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(async function (latest) {
self.channels_latest = Object.assign({}, self.channels_latest, {
'🔐': latest[0],
});
} finally {
this.loading_channels_latest--;
console.log('private took', (new Date() - start_time) / 1000.0);
self.private_messages = latest[1];
self.grouped_private_messages = await self.group_private_messages(
latest[1]
);
});
}
_schedule_load_latest_timer() {
--this.loading_latest_scheduled;
this.schedule_load_latest();
}
reset_progress() {
if (this.progress === undefined) {
this._progress_start = new Date();
requestAnimationFrame(this.update_progress.bind(this));
}
}
_schedule_load_channels_latest_timer() {
--this.loading_channels_latest_scheduled;
this.schedule_load_channels_latest();
update_progress() {
if (
!this.loading_latest &&
!this.loading_latest_scheduled &&
!this.shadowRoot.getElementById('tf-tab-news')?.is_loading()
) {
this.progress = undefined;
return;
}
this.progress = (new Date() - this._progress_start).valueOf();
requestAnimationFrame(this.update_progress.bind(this));
}
schedule_load_channels_latest() {
if (!this.loading_channels_latest) {
schedule_load_latest() {
this.reset_progress();
if (!this.loading_latest) {
this.shadowRoot.getElementById('tf-tab-news')?.load_latest();
this.load_channels_latest(this.following);
} else if (!this.loading_channels_latest_scheduled) {
this.loading_channels_latest_scheduled++;
setTimeout(this._schedule_load_channels_latest_timer.bind(this), 5000);
this.load();
} else if (!this.loading_latest_scheduled) {
this.loading_latest_scheduled++;
setTimeout(this._schedule_load_latest_timer.bind(this), 5000);
}
}
async fetch_user_info(users) {
let info = await tfrpc.rpc.query(
`
SELECT messages_stats.author, messages_stats.max_sequence, messages_stats.max_timestamp AS max_ts FROM messages_stats
JOIN json_each(?) AS following
ON messages_stats.author = following.value
`,
[JSON.stringify(Object.keys(users))]
);
for (let row of info) {
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() {
let start_time = new Date();
let whoami = this.whoami;
let following = await tfrpc.rpc.following([whoami], 2);
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,
};
by_count.push({count: v.of, id: id});
this.loading_latest = true;
this.reset_progress();
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] = 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();
start_time = new Date();
users = await this.fetch_user_info(users);
console.log(
'user info took',
(new Date() - start_time) / 1000.0,
'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 {
this.loading_latest = false;
}
this.load_channels_latest(Object.keys(following));
this.channels_unread = JSON.parse(
(await tfrpc.rpc.databaseGet('unread')) ?? '{}'
);
users = await this.fetch_about(Object.keys(following).sort(), users);
console.log(
'about took',
(new Date() - start_time) / 1000.0,
'seconds for',
Object.keys(users).length,
'users'
);
this.following = Object.keys(following);
this.users = users;
console.log(`load finished ${whoami} => ${this.whoami} in ${(new Date() - start_time) / 1000}`);
this.whoami = whoami;
this.loaded = whoami;
}
channel_set_unread(event) {
@@ -442,12 +689,21 @@ 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}
@refresh=${this.refresh}
@toggle_stay_connected=${this.toggle_stay_connected}
@loadmessages=${this.reset_progress}
@closeprivatechat=${this.close_private_chat}
.connections=${this.connections}
.private_messages=${this.private_messages}
.grouped_private_messages=${this.visible_private()}
.recent_reactions=${this.recent_reactions}
?is_administrator=${this.is_administrator}
?stay_connected=${this.stay_connected}
></tf-tab-news>
`;
} else if (this.tab === 'connections') {
@@ -486,6 +742,7 @@ class TfElement extends LitElement {
async set_tab(tab) {
this.tab = tab;
if (tab === 'news') {
this.schedule_load_latest();
await tfrpc.rpc.setHash('#');
} else if (tab === 'connections') {
await tfrpc.rpc.setHash('#connections');
@@ -498,6 +755,18 @@ class TfElement extends LitElement {
tfrpc.rpc.sync();
}
async toggle_stay_connected() {
let stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
let new_stay_connected = !this.stay_connected;
try {
if (new_stay_connected != stay_connected) {
await tfrpc.rpc.globalSettingsSet('stay_connected', new_stay_connected);
}
} finally {
this.stay_connected = await tfrpc.rpc.globalSettingsGet('stay_connected');
}
}
render() {
let self = this;
@@ -520,12 +789,26 @@ class TfElement extends LitElement {
class="w3-bar w3-theme-l1"
style="position: static; top: 0; z-index: 10"
>
<button
class="w3-bar-item w3-button w3-circle w3-ripple"
@click=${this.refresh}
>
</button>
${this.is_administrator && self.tab != 'news'
? html`
<button
class=${'w3-bar-item w3-button w3-circle w3-ripple' +
(this.connections?.some((x) => x.flags.one_shot)
? ' w3-spin'
: '')}
style="width: 1.5em; height: 1.5em; padding: 8px"
@click=${this.refresh}
>
</button>
<button
class="w3-bar-item w3-button w3-ripple"
@click=${this.toggle_stay_connected}
>
${this.stay_connected ? '🔗' : '⛓️‍💥'}
</button>
`
: undefined}
${Object.entries(k_tabs).map(
([k, v]) => html`
<button
@@ -544,8 +827,20 @@ class TfElement extends LitElement {
)}
</div>
`;
let contents =
!this.loaded || this.loading
let contents = this.guest
? html`<div
class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge w3-container"
>
<p>⚠️🦀 Must be logged in to Tilde Friends to scuttle here. 🦀⚠️</p>
<footer class="w3-center">
<a
class="w3-button w3-theme-d1"
href=${`/login?return=${encodeURIComponent(this.url)}`}
>Login</a
>
</footer>
</div>`
: !this.loaded || this.loading
? html`<div
class="w3-display-middle w3-panel w3-theme-l5 w3-card-4 w3-padding-large w3-round-xlarge w3-xlarge"
>
@@ -553,13 +848,25 @@ class TfElement extends LitElement {
Loading...
</div>`
: this.render_tab();
let progress =
this.progress !== undefined
? html`
<div style="position: absolute; width: 100%" id="progress">
<div
class="w3-theme-l3"
style=${`height: 4px; position: absolute; right: ${Math.cos(this.progress / 250) > 0 ? 'auto' : '0'}; width: ${50 * Math.sin(this.progress / 250) + 50}%`}
></div>
</div>
`
: undefined;
return html`
<div
style="width: 100vw; min-height: 100vh; height: 100vh; display: flex; flex-direction: column"
class="w3-theme-dark"
>
${progress}
<div style="flex: 0 0">${tabs}</div>
<div style="flex: 1 1; overflow: scroll; contain: layout">
<div style="flex: 1 1; overflow: auto; contain: layout">
${contents}
</div>
</div>

View File

@@ -16,6 +16,7 @@ class TfComposeElement extends LitElement {
author: {type: String},
channel: {type: String},
new_thread: {type: Boolean},
recipients: {type: Array},
};
}
@@ -91,7 +92,9 @@ class TfComposeElement extends LitElement {
bubbles: true,
composed: true,
detail: {
id: this.branch,
id:
this.branch ??
(this.recipients ? this.recipients.join(',') : undefined),
draft: draft,
},
})
@@ -255,10 +258,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();
}
@@ -289,7 +294,7 @@ class TfComposeElement extends LitElement {
}
}
firstUpdated() {
get_values() {
let values = Object.entries(this.users).map((x) => ({
key: x[1].name ?? x[0],
value: x[0],
@@ -305,11 +310,15 @@ class TfComposeElement extends LitElement {
values
);
}
return values;
}
firstUpdated() {
let tribute = new Tribute({
iframe: this.shadowRoot,
collection: [
{
values: values,
values: this.get_values(),
selectTemplate: function (item) {
return item
? `[@${item.original.key}](${item.original.value})`
@@ -328,6 +337,7 @@ class TfComposeElement extends LitElement {
],
});
tribute.attach(this.renderRoot.getElementById('edit'));
this._tribute = tribute;
}
updated() {
@@ -338,6 +348,7 @@ class TfComposeElement extends LitElement {
preview.innerHTML = this.process_text(edit.innerText);
this.last_updated_text = edit.innerText;
}
this._tribute.collection[0].values = this.get_values();
let encrypt = this.renderRoot.getElementById('encrypt_to');
if (encrypt) {
let tribute = new Tribute({
@@ -357,7 +368,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 +455,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 +484,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>
`;
}
}
@@ -500,7 +505,17 @@ class TfComposeElement extends LitElement {
}
get_draft() {
return this.drafts[this.branch || ''] || {};
let key =
this.branch ||
(this.recipients ? this.recipients.join(',') : undefined) ||
'';
let draft = this.drafts[key] || {};
if (this.recipients && !draft.encrypt_to?.length) {
draft.encrypt_to = [
...new Set(this.recipients).union(new Set(draft.encrypt_to ?? [])),
];
}
return draft;
}
update_encrypt(event) {
@@ -522,7 +537,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 +559,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,10 +597,10 @@ 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([])}
>
🔐
🔐 Encrypt
</button>`;
let result = html`
<style>
@@ -581,7 +621,7 @@ class TfComposeElement extends LitElement {
: undefined}
${this.render_encrypt()}
</header>
<div class="w3-container w3-padding-small">
<div class="w3-container" style="padding: 0 0 16px 0">
<div class="w3-half">
<span
class="w3-input w3-theme-d1 w3-border"
@@ -594,7 +634,7 @@ class TfComposeElement extends LitElement {
.innerText=${live(draft.text ?? '')}
></span>
</div>
<div class="w3-half">
<div class="w3-half w3-container">
${content_warning}
<p id="preview"></p>
</div>
@@ -602,19 +642,53 @@ class TfComposeElement extends LitElement {
${Object.values(draft.mentions || {}).map((x) =>
self.render_mention(x)
)}
<footer class="w3-container">
<footer>
${this.render_attach_app()} ${this.render_content_warning()}
${this.render_new_thread()}
<button class="w3-button w3-theme-d1" id="submit" @click=${this.submit}>
<button
class="w3-button w3-theme-d1"
id="submit"
@click=${this.submit}
>
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>
`;

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
import {LitElement, html, unsafeHTML, repeat, until} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import {styles} from './tf-styles.js';
@@ -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,20 +194,28 @@ 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`
<div>
${final_messages.map(
${repeat(
final_messages,
(x) => x.id,
(x) => html`
<tf-message
.message=${x}
@@ -209,13 +226,26 @@ 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">
<div
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
></div>
<div style="color: #f00; padding: 8px">unread</div>
<button
style="color: #f00; padding: 8px"
class="w3-button"
@click=${() =>
this.dispatchEvent(
new Event('mark_all_read', {
bubbles: true,
composed: true,
})
)}
>
unread
</button>
<div
style="border-bottom: 1px solid #f00; flex: 1; align-self: center; height: 1px"
></div>

View File

@@ -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,9 +11,10 @@ class TfProfileElement extends LitElement {
id: {type: String},
users: {type: Object},
size: {type: Number},
server_follows_me: {type: Boolean},
sequence: {type: Number},
following: {type: Boolean},
blocking: {type: Boolean},
show_followed: {type: Boolean},
};
}
@@ -27,7 +28,7 @@ class TfProfileElement extends LitElement {
this.id = null;
this.users = {};
this.size = 0;
this.server_follows_me = undefined;
this.sequence = 0;
}
async load() {
@@ -63,27 +64,8 @@ class TfProfileElement extends LitElement {
}
}
async initial_load() {
this.server_follows_me = undefined;
let server_id = await tfrpc.rpc.getServerIdentity();
let followed = await tfrpc.rpc.query(
`
SELECT json_extract(content, '$.following') AS following
FROM messages
WHERE author = ? AND
json_extract(content, '$.type') = 'contact' AND
json_extract(content, '$.contact') = ? ORDER BY sequence DESC LIMIT 1
`,
[server_id, this.whoami]
);
let is_followed = false;
for (let row of followed) {
is_followed = row.following != 0;
}
this.server_follows_me = is_followed;
}
modify(change) {
let self = this;
tfrpc.rpc
.appendMessage(
this.whoami,
@@ -95,6 +77,10 @@ class TfProfileElement extends LitElement {
change
)
)
.then(function () {
self._follow_whoami = undefined;
self.load();
})
.catch(function (error) {
alert(error?.message);
});
@@ -156,7 +142,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()
@@ -171,67 +158,98 @@ class TfProfileElement extends LitElement {
.catch(function (e) {
alert(e.message);
});
};
});
document.body.appendChild(input);
input.click();
}
async server_follow_me(follow) {
try {
await tfrpc.rpc.setServerFollowingMe(this.whoami, follow);
} catch (e) {
console.log(e);
}
try {
await this.initial_load();
} catch (e) {
console.log(e);
}
}
copy_id() {
navigator.clipboard.writeText(this.id);
}
render() {
if (
this.id == this.whoami &&
this.editing &&
this.server_follows_me === undefined
) {
this.initial_load();
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 = '100vw';
img.style.maxHeight = '100vh';
img.style.display = 'block';
img.style.margin = 'auto';
img.style.objectFit = 'contain';
img.style.width = '100vw';
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;
this.show_followed = !this.show_followed;
}
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 followed_accounts"
@click=${this.toggle_account_list}
>
${this.show_followed ? 'Hide' : 'Show'} Followed Accounts
(${Object.keys(accounts).length})
</button>
<div class=${'w3-card' + (this.show_followed ? '' : ' w3-hide')}>
<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;
let block;
if (this.id === this.whoami) {
if (this.editing) {
let server_follow;
if (this.server_follows_me === true) {
server_follow = html`<button
class="w3-button w3-theme-d1"
@click=${() => this.server_follow_me(false)}
>
Server, Stop Following Me
</button>`;
} else if (this.server_follows_me === false) {
server_follow = html`<button
class="w3-button w3-theme-d1"
@click=${() => this.server_follow_me(true)}
>
Server, Follow Me
</button>`;
}
edit = html`
<button
id="save_profile"
@@ -243,7 +261,6 @@ class TfProfileElement extends LitElement {
<button class="w3-button w3-theme-d1" @click=${this.discard_edits}>
Discard
</button>
${server_follow}
`;
} else {
edit = html`<button
@@ -276,38 +293,49 @@ class TfProfileElement extends LitElement {
let edit_profile = this.editing
? html`
<div style="flex: 1 0 50%; display: flex; flex-direction: column; gap: 8px">
<div class="w3-container">
<div>
<label for="name">Name:</label>
<input class="w3-input w3-theme-d1" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))}></input>
</div>
<div><label for="description">Description:</label></div>
<textarea class="w3-input w3-theme-d1" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))}>${this.editing.description}</textarea>
<div>
<label for="public_web_hosting">Public Web Hosting:</label>
<input class="w3-check w3-theme-d1" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
</div>
<div>
<button class="w3-button w3-theme-d1" @click=${this.attach_image}>Attach Image</button>
</div>
<div>
<label for="name">Name:</label>
<input class="w3-input w3-theme-d1" type="text" id="name" value=${this.editing.name} @input=${(event) => (this.editing = Object.assign({}, this.editing, {name: event.srcElement.value}))} placeholder="Choose a name"></input>
</div>
<div><label for="description">Description:</label></div>
<textarea class="w3-input w3-theme-d1" style="resize: vertical" rows="8" id="description" @input=${(event) => (this.editing = Object.assign({}, this.editing, {description: event.srcElement.value}))} placeholder="Tell people a little bit about yourself here, if you like.">${this.editing.description}</textarea>
<div>
<label for="public_web_hosting">Public Web Hosting:</label>
<input class="w3-check w3-theme-d1" type="checkbox" id="public_web_hosting" ?checked=${this.editing.publicWebHosting} @input=${(event) => (self.editing = Object.assign({}, self.editing, {publicWebHosting: event.srcElement.checked}))}></input>
</div>
<div>
<button class="w3-button w3-theme-d1" @click=${this.attach_image}>Attach Image</button>
</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">
<input type="text" class="w3-input w3-border w3-theme-d1" readonly value=${this.id}></input>
<button class="w3-button w3-theme-d1 w3-ripple" @click=${this.copy_id}>Copy</button>
<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>
</div>
<div style="display: flex; flex-direction: row; gap: 1em">
${edit_profile}
<div style="flex: 1 0 50%">
<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div>
<div style="flex: 1 0 50%; contain: layout; overflow: auto; word-wrap: normal; word-break: normal">
${
image
? html`<div><img src=${'/' + image + '/view'} style="width: 256px; height: auto"></img></div>`
: html`<div>
<div class="w3-jumbo">😎</div>
<div><i>Profile image not set.</i></div>
</div>`
}
<div>${unsafeHTML(tfutils.markdown(description))}</div>
</div>
</div>
@@ -318,8 +346,12 @@ 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>
<a class="w3-button w3-theme-d1" href=${'#🔐' + (this.id != this.whoami ? this.id : '')}>
Open Private Chat
</a>
${edit}
${follow}
${block}

View File

@@ -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>

View File

@@ -43,12 +43,14 @@ const tf = css`
border-left: 4px solid #fff;
padding: 8px;
padding-left: 12px;
margin-left: 0;
margin-right: 0;
}
`;
// 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 +90,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 +138,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 +160,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 +205,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 +222,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}
@@ -396,9 +411,9 @@ function is_dark(hex, value) {
function generated() {
let now = new Date();
let k_color = rgb_to_hex([
now.getDay() * 255 / 6,
now.getHours() * 255 / 23,
now.getSeconds() * 255 / 59,
(now.getDay() * 128) / 6,
(now.getHours() * 128) / 23,
(now.getSeconds() * 128) / 59,
]);
//let k_color = '#034f84';
//let k_color = rgb_to_hex([Math.random() * 256, Math.random() * 256, Math.random() * 256]);

View File

@@ -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`
@@ -142,14 +159,28 @@ class TfTabConnectionsElement extends LitElement {
}, {})
);
return html`
<button
class="w3-button w3-theme-d1"
@click=${() => tfrpc.rpc.closeConnection(connection.id)}
>
Close
</button>
${connection.connected
? html`
<button
class="w3-button w3-theme-d1"
@click=${() => tfrpc.rpc.closeConnection(connection.id)}
>
Close
</button>
`
: 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})`}
@@ -202,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`
@@ -216,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>
@@ -256,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)}
@@ -263,25 +321,33 @@ class TfTabConnectionsElement extends LitElement {
`
)}
</ul>
<h2>Local Accounts</h2>
<ul class="w3-ul w3-border">
<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`<li class="w3-bar">
html`<div
class="w3-tag w3-round w3-theme-l3"
style="padding: 4px; margin: 2px; max-width: 100%; text-wrap: nowrap; overflow: hidden"
>
${x == this.server_identity
? html`<span class="w3-tag w3-medium w3-round w3-theme-l1"
>🖥 local server</span
>`
? html`<div class="w3-tag w3-medium w3-round w3-theme-l1">
🖥 local server
</div>`
: undefined}
${this.my_identities.indexOf(x) != -1
? html`<span class="w3-tag w3-medium w3-round w3-theme-d1"
>😎 you</span
>`
? html`<div class="w3-tag w3-medium w3-round w3-theme-d1">
😎 you
</div>`
: undefined}
<tf-user id=${x} .users=${this.users}></tf-user>
</li>`
</div>`
)}
</ul>
</div>
</div>
`;
}

View File

@@ -1,4 +1,4 @@
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
import {LitElement, cache, html, unsafeHTML, until} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import {styles} from './tf-styles.js';
@@ -17,6 +17,9 @@ class TfTabNewsFeedElement extends LitElement {
loading: {type: Number},
time_range: {type: Array},
time_loading: {type: Array},
private_messages: {type: Array},
grouped_private_messages: {type: Object},
recent_reactions: {type: Array},
};
}
@@ -36,6 +39,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,9 +49,73 @@ 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.dispatchEvent(
new CustomEvent('loadmessages', {
bubbles: true,
composed: true,
})
);
this.time_loading = [start_time, end_time];
let result;
const k_max_results = 64;
if (this.hash == '#@') {
result = await tfrpc.rpc.query(
`
@@ -57,53 +125,49 @@ class TfTabNewsFeedElement extends LitElement {
JOIN json_each(?2) AS following ON messages.author = following.value
WHERE
messages.author != ?1 AND
messages.timestamp >= ?3 AND
messages.timestamp < ?4
ORDER BY timestamp DESC limit 20)
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
(?3 IS NULL OR messages.timestamp >= ?3) AND messages.timestamp < ?4
ORDER BY timestamp DESC limit ?5)
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 mentions
JOIN messages_refs ON mentions.id = messages_refs.ref
JOIN messages ON messages_refs.message = messages.id
UNION
SELECT * FROM mentions
SELECT TRUE AS is_primary, * FROM mentions
`,
[
'"' + this.whoami.replace('"', '""') + '"',
JSON.stringify(this.following),
start_time,
end_time,
k_max_results,
]
);
} else if (this.hash.startsWith('#@')) {
result = await tfrpc.rpc.query(
`
WITH mine AS (SELECT rowid, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
FROM messages
WHERE messages.author = ?
ORDER BY sequence DESC)
SELECT messages.rowid, messages.id, messages.previous, messages.author, messages.sequence, messages.timestamp, messages.hash, json(messages.content) AS content, messages.signature
FROM mine
JOIN messages_refs ON mine.id = messages_refs.ref
WITH
selected AS (SELECT rowid, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
FROM messages
WHERE messages.author = ?1 AND (?2 IS NULL OR messages.timestamp >= 2) AND messages.timestamp < ?3
ORDER BY sequence DESC LIMIT ?4
)
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 selected
JOIN messages_refs ON selected.id = messages_refs.ref
JOIN messages ON messages_refs.message = messages.id
WHERE
mine.timestamp >= ?2 AND
mine.timestamp < ?3
UNION
SELECT * FROM mine
WHERE
mine.timestamp >= ?2 AND
mine.timestamp < ?3
SELECT TRUE AS is_primary, * FROM selected
`,
[this.hash.substring(1), start_time, end_time]
[this.hash.substring(1), start_time, end_time, k_max_results]
);
} else if (this.hash.startsWith('#%')) {
result = await tfrpc.rpc.query(
`
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
SELECT TRUE AS is_primary, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
FROM messages
WHERE id = ?1
WHERE messages.id = ?1
UNION
SELECT id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
SELECT FALSE AS is_primary, id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
FROM messages JOIN messages_refs
ON messages.id = messages_refs.message
WHERE messages_refs.ref = ?1
@@ -111,134 +175,142 @@ class TfTabNewsFeedElement extends LitElement {
[this.hash.substring(1)]
);
} else if (this.hash.startsWith('##')) {
let promises = [];
const k_following_limit = 256;
for (let i = 0; i < this.following.length; i += k_following_limit) {
promises.push(
tfrpc.rpc.query(
`
WITH 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
messages.timestamp >= ? AND
messages.timestamp < ? AND
messages.content ->> 'channel' = ?
ORDER BY messages.timestamp DESC)
SELECT 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 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 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
JOIN json_each(?1) AS following ON messages.author = following.value
JOIN json_tree(messages.content, '$.mentions') AS mention ON mention.value = '#' || ?4
WHERE
messages.timestamp >= ?2 AND
messages.timestamp < ?3
UNION
SELECT news.* FROM news
`,
[
JSON.stringify(this.following.slice(i, i + k_following_limit)),
start_time,
end_time,
this.hash.substring(2),
'"#' + this.hash.substring(2).replace('"', '""') + '"',
]
)
);
}
result = [].concat(...(await Promise.all(promises)));
} else if (this.hash == '#🔐') {
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 messages.content ->> 'channel' = ?4 AND messages.content ->> 'type' != 'vote'
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_refs
JOIN messages ON messages.id = messages_refs.message
JOIN json_each(?1) AS following ON messages.author = following.value
WHERE messages_refs.ref = '#' || ?4 AND messages.content ->> 'type' != 'vote'
)
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 ?5
`,
[
JSON.stringify(this.following),
start_time,
end_time,
this.hash.substring(2),
k_max_results,
]
);
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.startsWith('#🔐')) {
let ids =
this.hash == '#🔐' ? [] : this.hash.substring('#🔐'.length).split(',');
result = await tfrpc.rpc.query(
`
SELECT messages.rowid, messages.id, previous, author, sequence, timestamp, hash, json(content) AS content, signature
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 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.rowid DESC LIMIT ?4
`,
[
JSON.stringify(
this.grouped_private_messages?.[JSON.stringify(ids)]?.map(
(x) => x.id
) ?? []
),
start_time,
end_time,
k_max_results,
]
);
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.timestamp >= ?2 AND
messages.timestamp < ?3 AND
json(messages.content) LIKE '"%'
ORDER BY sequence DESC
messages.content ->> 'type' = 'vote' AND
(?2 IS NULL OR messages.timestamp >= ?2) AND messages.timestamp < ?3
ORDER BY timestamp DESC limit ?4)
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]
[JSON.stringify(this.following), start_time, end_time, k_max_results]
);
result = (await this.decrypt(result)).filter((x) => x.decrypted);
} else {
let promises = [];
const k_following_limit = 256;
for (let i = 0; i < this.following.length; i += k_following_limit) {
promises.push(
tfrpc.rpc.query(
`
WITH 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 messages.timestamp >= ? AND messages.timestamp < ?
ORDER BY messages.timestamp DESC)
SELECT 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 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 news.* FROM news
`,
[
JSON.stringify(this.following.slice(i, i + k_following_limit)),
start_time,
end_time,
]
)
);
}
result = [].concat(...(await Promise.all(promises)));
let t0 = new Date();
let initial_messages = await tfrpc.rpc.query(
`
SELECT TRUE 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(?) AS following ON messages.author = following.value
WHERE messages.timestamp < ?3 AND (?2 IS NULL OR messages.timestamp >= ?2) AND
messages.content ->> 'type' != 'vote'
ORDER BY timestamp DESC LIMIT ?4
`,
[JSON.stringify(this.following), start_time, end_time, k_max_results]
);
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;
}
update_time_range_from_messages(messages) {
let only_primary = messages.filter((x) => x.is_primary);
this.time_range = [
messages.reduce(
only_primary.reduce(
(accumulator, current) => Math.min(accumulator, current.timestamp),
this.time_range[0]
),
messages.reduce(
only_primary.reduce(
(accumulator, current) => Math.max(accumulator, current.timestamp),
this.time_range[1]
),
];
}
unread_allowed() {
return (
this.hash == '#@' ||
(!this.hash.startsWith('#%') && !this.hash.startsWith('#@'))
);
}
async load_more() {
this.loading++;
this.loading_canceled = false;
try {
let more = [];
while (!more.length && !this.loading_canceled) {
let last_start_time = this.start_time;
this.start_time = last_start_time - 7 * 24 * 60 * 60 * 1000;
more = await this.fetch_messages(this.start_time, last_start_time);
this.update_time_range_from_messages(
more.filter(
(x) =>
x.timestamp >= this.start_time && x.timestamp < last_start_time
)
);
let last_start_time = this.time_range[0];
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)
);
this.messages = await this.decrypt([...more, ...this.messages]);
} finally {
this.loading--;
@@ -274,80 +346,75 @@ class TfTabNewsFeedElement extends LitElement {
return result;
}
merge_messages(old_messages, new_messages) {
let old_by_id = Object.fromEntries(old_messages.map((x) => [x.id, x]));
return new_messages.map((x) => (old_by_id[x.id] ? old_by_id[x.id] : x));
}
async load_latest() {
this.loading++;
let now = new Date().valueOf();
let end_time = now + 24 * 60 * 60 * 1000;
let messages = [];
try {
messages = await this.fetch_messages(
this.time_range[1] - 24 * 60 * 60 * 1000,
end_time
);
messages = await this.fetch_messages(this.time_range[0], end_time);
messages = await this.decrypt(messages);
this.update_time_range_from_messages(
messages.filter(
(x) => x.timestamp >= this.time_range[1] && x.timestamp < end_time
(x) => x.timestamp >= this.time_range[0] && x.timestamp < end_time
)
);
} finally {
this.loading--;
}
this.messages = Object.values(
Object.fromEntries(
[...this.messages, ...messages]
.sort((x, y) => x.timestamp - y.timestamp)
.slice(-1024)
.map((x) => [x.id, x])
this.messages = this.merge_messages(
this.messages,
Object.values(
Object.fromEntries(
[...this.messages, ...messages]
.sort((x, y) => x.timestamp - y.timestamp)
.slice(-1024)
.map((x) => [x.id, x])
)
)
);
console.log('done loading latest messages.');
}
async load_messages() {
let start_time = new Date();
let self = this;
this.loading++;
let messages = [];
let original_hash = this.hash;
try {
this.messages = [];
this._messages_hash = this.hash;
this._messages_following = this.following;
if (this._messages_hash !== this.hash) {
this.messages = [];
this._messages_hash = this.hash;
}
this._messages_following = JSON.stringify(this.following);
this._private_messages =
JSON.stringify(this.private_messages) +
JSON.stringify(this.grouped_private_messages);
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];
messages = await this.fetch_messages(
this.time_range[0],
this.time_range[1]
);
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[0] &&
x.timestamp < this.time_range[1]
)
messages.filter((x) => x.timestamp < this.time_range[1])
);
messages = await this.decrypt(messages);
if (!messages.length) {
let more = [];
while (!more.length && start_time >= 0) {
let last_start_time = start_time;
start_time = last_start_time - 7 * 24 * 60 * 60 * 1000;
more = await this.fetch_messages(start_time, last_start_time);
this.update_time_range_from_messages(
more.filter(
(x) => x.timestamp >= start_time && x.timestamp < last_start_time
)
);
}
messages = await this.decrypt([...more, ...this.messages]);
}
} finally {
this.loading--;
}
this.messages = messages;
if (this.hash == original_hash) {
this.messages = this.merge_messages(this.messages, messages);
}
this.time_loading = undefined;
console.log(`loading messages done for ${self.whoami}`);
console.log(
`loading ${messages.length} messages done for ${self.whoami} in ${(new Date() - start_time) / 1000}s`
);
}
mark_all_read() {
@@ -369,11 +436,42 @@ class TfTabNewsFeedElement extends LitElement {
}
}
close_private_chat() {
this.mark_all_read();
this.dispatchEvent(
new CustomEvent('closeprivatechat', {
bubbles: true,
composed: true,
detail: {
key: JSON.stringify(
this.hash == '#🔐'
? []
: this.hash.substring('#🔐'.length).split(',')
),
},
})
);
tfrpc.rpc.setHash('#');
}
render_close_chat_button() {
if (this.hash.startsWith('#🔐')) {
return html`
<button class="w3-button w3-theme-d1" @click=${this.close_private_chat}>
Close Chat
</button>
`;
}
}
render() {
if (
!this.messages ||
this._messages_hash !== this.hash ||
this._messages_following !== this.following
this._messages_following !== JSON.stringify(this.following) ||
this._private_messages !==
JSON.stringify(this.private_messages) +
JSON.stringify(this.grouped_private_messages)
) {
console.log(
`loading messages for ${this.whoami} (following ${this.following.length})`
@@ -384,9 +482,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"
@@ -417,10 +522,16 @@ class TfTabNewsFeedElement extends LitElement {
</p>
`;
}
return html`
<button class="w3-button w3-theme-d1" @click=${this.mark_all_read}>
Mark All Read
</button>
return cache(html`
${this.unread_allowed()
? html`<button
class="w3-button w3-theme-d1"
@click=${this.mark_all_read}
>
Mark All Read
</button>`
: undefined}
${this.render_close_chat_button()}
<tf-news
id="news"
whoami=${this.whoami}
@@ -429,11 +540,14 @@ 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}
@mark_all_read=${this.mark_all_read}
></tf-news>
${more}
`;
`);
}
}

View File

@@ -1,4 +1,11 @@
import {LitElement, html, unsafeHTML, until} from './lit-all.min.js';
import {
LitElement,
cache,
keyed,
html,
unsafeHTML,
until,
} from './lit-all.min.js';
import * as tfrpc from '/static/tfrpc.js';
import {styles} from './tf-styles.js';
@@ -16,6 +23,12 @@ class TfTabNewsElement extends LitElement {
channels_unread: {type: Object},
channels_latest: {type: Object},
connections: {type: Array},
private_messages: {type: Array},
grouped_private_messages: {type: Object},
recent_reactions: {type: Array},
peer_exchange: {type: Boolean},
is_administrator: {type: Boolean},
stay_connected: {type: Boolean},
};
}
@@ -35,9 +48,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() {
@@ -50,6 +65,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) {
@@ -87,7 +110,26 @@ 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 (channel?.startsWith('🔐')) {
let key = JSON.stringify(channel.substring('🔐'.length).split(','));
if (this.grouped_private_messages?.[key]) {
let grouped_latest = Math.max(
...this.grouped_private_messages?.[key]?.map((x) => x.rowid)
);
if (
this.channels_unread[channel] === undefined ||
grouped_latest > this.channels_unread[channel]
) {
return '✉️ ';
}
}
} else if (
this.channels_latest[channel] &&
this.channels_latest[channel] > 0 &&
(this.channels_unread[channel] === undefined ||
@@ -128,6 +170,34 @@ class TfTabNewsElement extends LitElement {
return this.hash.startsWith('##') ? this.hash.substring(2) : undefined;
}
compare_follows(a, b) {
return b[1].ts > a[1].ts ? 1 : b[1].ts < a[1].ts ? -1 : 0;
}
suggested_follows() {
/*
** Filter out people who have used future timestamps so that they aren't
** pinned at the top.
*/
let self = this;
let now = new Date().valueOf();
return Object.entries(this.users)
.filter((x) => x[1].ts < now)
.filter((x) => x[1].follow_depth > 1)
.sort(self.compare_follows)
.slice(0, 8)
.map((x) => x[0]);
}
async enable_peer_exchange() {
await tfrpc.rpc.globalSettingsSet('peer_exchange', true);
await this.check_peer_exchange();
}
is_loading() {
return this.shadowRoot?.getElementById('news')?.loading;
}
render_sidebar() {
return html`
<div
@@ -141,6 +211,35 @@ class TfTabNewsElement extends LitElement {
>
&times;
</div>
${this.is_administrator
? html`
<button
class="w3-bar-item w3-button"
@click=${() =>
this.dispatchEvent(
new Event('refresh', {bubbles: true, composed: true})
)}
>
<span style="display: inline-block; width: 1.8em">↻</span>
Sync now
</button>
<button
class="w3-bar-item w3-button w3-ripple"
@click=${() =>
this.dispatchEvent(
new Event('toggle_stay_connected', {
bubbles: true,
composed: true,
})
)}
>
<span style="display: inline-block; width: 1.8em"
>${this.stay_connected ? '🔗' : '⛓️‍💥'}</span
>
${this.stay_connected ? 'Online mode' : 'Passive mode'}
</button>
`
: undefined}
${this.hash.startsWith('##') &&
this.channels.indexOf(this.hash.substring(2)) == -1
? html`
@@ -153,7 +252,7 @@ class TfTabNewsElement extends LitElement {
>
`
: undefined}
<div class="w3-bar-item w3-theme-d2">Channels</div>
<h4 class="w3-bar-item w3-theme-d2">Channels</h4>
<a
href="#"
class="w3-bar-item w3-button"
@@ -167,11 +266,34 @@ class TfTabNewsElement extends LitElement {
>${this.unread_status('@')}@mentions</a
>
<a
href="#🔐"
href="#👍"
class="w3-bar-item w3-button"
style=${this.hash == '#🔐' ? 'font-weight: bold' : undefined}
>${this.unread_status('🔐')}🔐private</a
style=${this.hash == '#👍' ? 'font-weight: bold' : undefined}
>${this.unread_status('👍')}👍votes</a
>
${Object.keys(this?.grouped_private_messages ?? [])
?.sort()
?.map(
(key) => html`
<a
href=${'#🔐' + JSON.parse(key).join(',')}
class="w3-bar-item w3-button"
style=${this.hash == '#🔐' + JSON.parse(key).join(',')
? 'font-weight: bold'
: undefined}
>${this.unread_status('🔐' + JSON.parse(key).join(','))}
${(key != '[]' ? JSON.parse(key) : [this.whoami]).map(
(id) => html`
<tf-user
id=${id}
nolink="true"
.users=${this.users}
></tf-user>
`
)}</a
>
`
)}
${Object.keys(this.drafts)
.sort()
.map(
@@ -194,11 +316,63 @@ class TfTabNewsElement extends LitElement {
>
`
)}
<div class="w3-bar-item w3-theme-d2">Connections</div>
${this.connections.map((x) => (html`
<tf-user class="w3-bar-item" style="max-width: 100%" id=${x.id} .users=${this.users}></tf-user>
`))}
<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.dispatchEvent(
new Event('refresh', {bubbles: true, composed: true})
)}
>
↻ 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)
.map(
(x) => html`
<tf-user
class="w3-bar-item"
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>
`
)}
<h4 class="w3-bar-item w3-theme-d2">Suggested Follows</h4>
${this.suggested_follows().map(
(x) => html`
<tf-user
class="w3-bar-item"
style="max-width: 100%"
id=${x}
.users=${this.users}
></tf-user>
`
)}
</div>
<div
class="w3-overlay"
@@ -211,12 +385,15 @@ class TfTabNewsElement extends LitElement {
render() {
let profile =
this.hash.startsWith('#@') && this.hash != '#@'
? html`<tf-profile
class="tf-profile"
id=${this.hash.substring(1)}
whoami=${this.whoami}
.users=${this.users}
></tf-profile>`
? keyed(
this.hash.substring(1),
html`<tf-profile
class="tf-profile"
id=${this.hash.substring(1)}
whoami=${this.whoami}
.users=${this.users}
></tf-profile>`
)
: undefined;
let edit_profile;
if (
@@ -231,10 +408,10 @@ class TfTabNewsElement extends LitElement {
name.
</div>`;
}
return html`
return cache(html`
${this.render_sidebar()}
<div
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: scroll"
style="margin-left: 2in; padding: 0px; top: 0; max-height: 100%; overflow: auto; contain: layout"
id="main"
class="w3-main"
>
@@ -259,7 +436,7 @@ class TfTabNewsElement extends LitElement {
class="w3-button w3-hide-large"
@click=${this.show_sidebar}
>
&#9776;
${this.unread_status()}&#9776;
</div>
Welcome, <tf-user id=${this.whoami} .users=${this.users}></tf-user>!
${edit_profile}
@@ -272,6 +449,9 @@ class TfTabNewsElement extends LitElement {
.drafts=${this.drafts}
@tf-draft=${this.draft}
.channel=${this.channel()}
.recipients=${this.hash.startsWith('#🔐')
? this.hash.substring('#🔐'.length).split(',')
: undefined}
></tf-compose>
</div>
${profile}
@@ -287,10 +467,13 @@ class TfTabNewsElement extends LitElement {
@tf-expand=${this.on_expand}
.channels_unread=${this.channels_unread}
.channels_latest=${this.channels_latest}
.private_messages=${this.private_messages}
.grouped_private_messages=${this.grouped_private_messages}
.recent_reactions=${this.recent_reactions}
></tf-tab-news-feed>
</div>
</div>
`;
`);
}
}

View File

@@ -19,9 +19,9 @@ class TfTagElement extends LitElement {
let number = this.count ? html` (${this.count})` : undefined;
return html`<a
href=${'#' + encodeURIComponent(this.tag)}
style="display: inline-block; margin: 3px; border: 1px solid black; background-color: #444; padding: 4px; border-radius: 3px"
class="w3-tag w3-theme-d1 w3-round-4 w3-button"
>${this.tag}${number}</a
>`;
> `;
}
}

View File

@@ -6,7 +6,10 @@ class TfUserElement extends LitElement {
static get properties() {
return {
id: {type: String},
fallback_name: {type: String},
icon_only: {type: Boolean},
users: {type: Object},
nolink: {type: Boolean},
};
}
@@ -15,31 +18,50 @@ class TfUserElement extends LitElement {
constructor() {
super();
this.id = null;
this.fallback_name = null;
this.icon_only = false;
this.users = {};
}
render() {
let user = this.users[this.id];
let shape =
user?.follow_depth === undefined || user.follow_depth >= 2
? 'w3-circle'
: 'w3-round';
let image = html`<span
class="w3-theme-light w3-circle"
class=${'w3-theme-l4 ' + shape}
style="display: inline-block; width: 2em; height: 2em; text-align: center; line-height: 2em"
>?</span
>😎</span
>`;
let name = this.users?.[this.id]?.name;
name = html`<a target="_top" href=${'#' + this.id}>${name !== undefined ? name : this.id}</a>`
let name_string = name ?? this.fallback_name ?? this.id;
name = this.icon_only
? undefined
: !this.nolink
? html`<a target="_top" href=${'#' + this.id}>${name_string}</a>`
: html`<span>${name_string}</span>`;
if (this.users[this.id]) {
let image_link = this.users[this.id].image;
image_link =
typeof image_link == 'string' ? image_link : image_link?.link;
if (user) {
let image_link = user.image;
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-circle"
class=${'w3-theme-l4 ' + shape}
style="width: 2em; height: 2em; vertical-align: middle; object-fit: cover"
src="/${image_link}/view"
title=${name_string + ' (' + this.id + ')'}
/>`;
}
}
return html` <div style="display: inline-block; vertical-align: middle; font-weight: bold; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis">
return html` <div
style=${'display: inline-block; vertical-align: middle; text-wrap: nowrap; max-width: 100%; overflow: hidden; text-overflow: ellipsis' +
(this.nolink ? '' : '; font-weight: bold')}
>
${image} ${name}
</div>`;
}

View File

@@ -50,9 +50,9 @@ function image(node, entering) {
'</div>'
);
if (this.options.safe && potentiallyUnsafe(node.destination)) {
this.lit('<img src="" alt="');
this.lit('<img src="" title="');
} else {
this.lit('<img src="' + this.esc(node.destination) + '" alt="');
this.lit('<img src="' + this.esc(node.destination) + '" title="');
}
}
this.disableTags += 1;

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "💾",
"previous": "&mvGTlWKFR5QM/3nb4fJ2WQq0n/gNKvBmhGDkAvb8ki8=.sha256"
"previous": "&tzZFIe7Y54O4sx1QtAPdemkXh+p5qHXSG/dlS7NP6OQ=.sha256"
}

View File

@@ -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
View File

@@ -0,0 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "🕸",
"previous": "&n7hu5b8/TsfiG6FDlCRG5nPCrIdCr96+xpIJ/aQT/uM=.sha256"
}

100
apps/web/app.js Normal file
View 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
View 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',
});
});

View File

@@ -1,5 +1,5 @@
{
"type": "tildefriends-app",
"emoji": "👋",
"previous": "&7gFmLW5zSMhmxWWY1+jeRcHdullgujSqGJg94lVgr1k=.sha256"
"previous": "&5NkMRSgcMqCYF3xcLOBmaytkoxfV9zx4br7JladKPTs=.sha256"
}

View File

@@ -1,5 +0,0 @@
async function main() {
await app.setDocument(utf8Decode(getFile('index.html')));
}
main();

1
apps/welcome/gitea.svg Normal file
View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" viewBox="0 0 640 640" width="32" height="32"><path d="m395.9 484.2-126.9-61c-12.5-6-17.9-21.2-11.8-33.8l61-126.9c6-12.5 21.2-17.9 33.8-11.8 17.2 8.3 27.1 13 27.1 13l-.1-109.2 16.7-.1.1 117.1s57.4 24.2 83.1 40.1c3.7 2.3 10.2 6.8 12.9 14.4 2.1 6.1 2 13.1-1 19.3l-61 126.9c-6.2 12.7-21.4 18.1-33.9 12" style="fill:#fff"/><path d="M622.7 149.8c-4.1-4.1-9.6-4-9.6-4s-117.2 6.6-177.9 8c-13.3.3-26.5.6-39.6.7v117.2c-5.5-2.6-11.1-5.3-16.6-7.9 0-36.4-.1-109.2-.1-109.2-29 .4-89.2-2.2-89.2-2.2s-141.4-7.1-156.8-8.5c-9.8-.6-22.5-2.1-39 1.5-8.7 1.8-33.5 7.4-53.8 26.9C-4.9 212.4 6.6 276.2 8 285.8c1.7 11.7 6.9 44.2 31.7 72.5 45.8 56.1 144.4 54.8 144.4 54.8s12.1 28.9 30.6 55.5c25 33.1 50.7 58.9 75.7 62 63 0 188.9-.1 188.9-.1s12 .1 28.3-10.3c14-8.5 26.5-23.4 26.5-23.4S547 483 565 451.5c5.5-9.7 10.1-19.1 14.1-28 0 0 55.2-117.1 55.2-231.1-1.1-34.5-9.6-40.6-11.6-42.6M125.6 353.9c-25.9-8.5-36.9-18.7-36.9-18.7S69.6 321.8 60 295.4c-16.5-44.2-1.4-71.2-1.4-71.2s8.4-22.5 38.5-30c13.8-3.7 31-3.1 31-3.1s7.1 59.4 15.7 94.2c7.2 29.2 24.8 77.7 24.8 77.7s-26.1-3.1-43-9.1m300.3 107.6s-6.1 14.5-19.6 15.4c-5.8.4-10.3-1.2-10.3-1.2s-.3-.1-5.3-2.1l-112.9-55s-10.9-5.7-12.8-15.6c-2.2-8.1 2.7-18.1 2.7-18.1L322 273s4.8-9.7 12.2-13c.6-.3 2.3-1 4.5-1.5 8.1-2.1 18 2.8 18 2.8L467.4 315s12.6 5.7 15.3 16.2c1.9 7.4-.5 14-1.8 17.2-6.3 15.4-55 113.1-55 113.1" style="fill:#609926"/><path d="M326.8 380.1c-8.2.1-15.4 5.8-17.3 13.8s2 16.3 9.1 20c7.7 4 17.5 1.8 22.7-5.4 5.1-7.1 4.3-16.9-1.8-23.1l24-49.1c1.5.1 3.7.2 6.2-.5 4.1-.9 7.1-3.6 7.1-3.6 4.2 1.8 8.6 3.8 13.2 6.1 4.8 2.4 9.3 4.9 13.4 7.3.9.5 1.8 1.1 2.8 1.9 1.6 1.3 3.4 3.1 4.7 5.5 1.9 5.5-1.9 14.9-1.9 14.9-2.3 7.6-18.4 40.6-18.4 40.6-8.1-.2-15.3 5-17.7 12.5-2.6 8.1 1.1 17.3 8.9 21.3s17.4 1.7 22.5-5.3c5-6.8 4.6-16.3-1.1-22.6 1.9-3.7 3.7-7.4 5.6-11.3 5-10.4 13.5-30.4 13.5-30.4.9-1.7 5.7-10.3 2.7-21.3-2.5-11.4-12.6-16.7-12.6-16.7-12.2-7.9-29.2-15.2-29.2-15.2s0-4.1-1.1-7.1c-1.1-3.1-2.8-5.1-3.9-6.3 4.7-9.7 9.4-19.3 14.1-29-4.1-2-8.1-4-12.2-6.1-4.8 9.8-9.7 19.7-14.5 29.5-6.7-.1-12.9 3.5-16.1 9.4-3.4 6.3-2.7 14.1 1.9 19.8z" style="fill:#609926"/></svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 86 KiB

View File

@@ -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,39 @@
<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"
>
<img src="gitea.svg" style="height: 1em; margin: 0" />
Development
</a>
<a
class="w3-button w3-black w3-padding-large"
href="https://docs.tildefriends.net/"
><i class="fa fa-book"></i> Documentation</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://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" />
@@ -107,15 +78,119 @@
<h2>First-time user checklist:</h2>
<ol type="1" style="text-align: left">
<li>
<a href="https://dev.tildefriends.net/cory/tildefriends/releases"
<a
href="https://dev.tildefriends.net/cory/tildefriends/releases/latest"
>Download</a
>
Tilde Friends or use
<a href="https://www.tildefriends.net/"
>https://www.tildefriends.net/</a
>.
<div class="w3-cell-row">
<div class="w3-container w3-cell w3-mobile">
<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 w3-mobile">
<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>
<h3>PeachCloud</h3>
<p>
Tilde Friends is also a part of 🍑☁️<a
href="https://peach-docs.commoninternet.net/"
>PeachCloud</a
>, which is available on
<a href="https://apps.yunohost.org/app/peachpub"
>YunoHost</a
>
for accessible self-hosting.
</p>
</div>
<div class="w3-container w3-cell w3-mobile">
<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 +224,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 +298,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
View 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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 KiB

After

Width:  |  Height:  |  Size: 141 KiB

View File

@@ -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}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,53 +1,48 @@
/**
* \file
* \defgroup tfapp Tilde Friends App JS
* Tilde Friends server-side app wrapper.
* @{
*/
/** \cond */
import * as core from './core.js';
let g_next_id = 1;
let g_calls = {};
export {App};
/** \endcond */
let gSessionIndex = 0;
/** A sequence number of apps. */
let g_session_index = 0;
/**
* TODOC
* @returns
*/
function makeSessionId() {
return 'session_' + (gSessionIndex++).toString();
}
/**
* TODOC
* @returns
** App constructor.
** @return An app instance.
*/
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
** Create a function wrapper that when called invokes a function on the app
** itself.
** @param api The function and argument names.
** @return A function.
*/
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,
@@ -60,8 +55,8 @@ App.prototype.makeFunction = function (api) {
};
/**
* TODOC
* @param {*} message
** Send a message to the app.
** @param message The message to send.
*/
App.prototype.send = function (message) {
if (this._send_queue) {
@@ -78,12 +73,11 @@ App.prototype.send = function (message) {
};
/**
* TODOC
* @param {*} request
* @param {*} response
* @param {*} client
** App socket handler.
** @param request The HTTP request of the WebSocket connection.
** @param response The HTTP response.
*/
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 +97,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 +123,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 +164,7 @@ async function socket(request, response, client) {
options.packageOwner = packageOwner;
options.packageName = packageName;
options.url = message.url;
let sessionId = makeSessionId();
let sessionId = 'session_' + (g_session_index++).toString();
if (blobId) {
if (message.edit_only) {
response.send(
@@ -176,9 +176,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 +222,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 +247,6 @@ async function socket(request, response, client) {
};
response.upgrade(100, {});
}
};
export {socket, App};
/** @} */

File diff suppressed because it is too large Load Diff

View File

@@ -1,33 +1,47 @@
/**
* \file
* \defgroup tfcore Tilde Friends Core JS
* Tilde Friends process management, in JavaScript.
* @{
*/
/** \cond */
import * as app from './app.js';
import * as form from './form.js';
import * as http from './http.js';
export {invoke, getProcessBlob};
/** \endcond */
/** All running processes. */
let gProcesses = {};
/** Whether stats are currently being sent. */
let gStatsTimer = false;
let kPingInterval = 60 * 1000;
/** Effectively a process ID. */
let g_handler_index = 0;
/** Time between pings, in milliseconds. */
const k_ping_interval = 60 * 1000;
/**
* TODOC
* @param {*} out
* @param {*} error
* Print an error.
* @param error The error.
*/
function printError(out, error) {
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
* Invoke a handler.
* @param handlers The handlers on which to invoke the callback.
* @param argv Arguments to pass to the handlers.
* @return A promise.
*/
function invoke(handlers, argv) {
let promises = [];
@@ -50,10 +64,10 @@ function invoke(handlers, argv) {
}
/**
* TODOC
* @param {*} eventName
* @param {*} argv
* @returns
* Broadcast a named event to all registered apps.
* @param eventName the name of the event.
* @param argv Arguments to pass to the handlers.
* @return A promise.
*/
function broadcastEvent(eventName, argv) {
let promises = [];
@@ -66,9 +80,9 @@ function broadcastEvent(eventName, argv) {
}
/**
* TODOC
* @param {*} message
* @returns
* Send a message to all other instances of the same app.
* @param message The message.
* @return A promise.
*/
function broadcast(message) {
let sender = this;
@@ -87,10 +101,13 @@ function broadcast(message) {
}
/**
* TODOC
* @param {String} eventName
* @param {*} argv
* @returns
* Send a message to all instances of the same app running as the same user.
* @param user The user.
* @param packageOwner The owner of the app.
* @param packageName The name of the app.
* @param eventName The name of the event.
* @param argv The arguments to pass.
* @return A promise.
*/
function broadcastAppEventToUser(
user,
@@ -115,10 +132,9 @@ function broadcastAppEventToUser(
}
/**
* TODOC
* @param {*} caller
* @param {*} process
* @returns
* Get user context information for a call.
* @param caller The calling process.
* @param process The receiving process.
*/
function getUser(caller, process) {
return {
@@ -131,43 +147,11 @@ function getUser(caller, process) {
}
/**
* TODOC
* @param {*} user
* @param {*} process
* @returns
*/
async function getApps(user, process) {
if (
process.credentials &&
process.credentials.session &&
process.credentials.session.name
) {
if (user && user !== process.credentials.session.name && user !== 'core') {
return {};
} else if (!user) {
user = process.credentials.session.name;
}
}
if (user) {
let db = new Database(user);
try {
let names = JSON.parse(await db.get('apps'));
let result = {};
for (let name of names) {
result[name] = await db.get('path:' + name);
}
return result;
} catch {}
}
return {};
}
/**
* TODOC
* @param {*} from
* @param {*} to
* @param {*} message
* @returns
* Send a message.
* @param from The calling process.
* @param to The receiving process.
* @param message The message.
* @return A promise.
*/
function postMessageInternal(from, to, message) {
if (to.eventHandlers['message']) {
@@ -176,14 +160,13 @@ function postMessageInternal(from, to, message) {
}
/**
* TODOC
* @param {*} blobId
* @param {*} key
* @param {*} options
* @returns
* Get or create a process for an app blob.
* @param blobId The blob identifier.
* @param key A unique key for the invocation.
* @param options Other options.
* @return The process.
*/
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 +185,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;
@@ -274,7 +257,6 @@ async function getProcessBlob(blobId, key, options) {
let settings = await loadSettings();
return settings?.permissions?.[user] ?? [];
},
apps: (user) => getApps(user, process),
getSockets: getSockets,
permissionTest: async function (permission) {
let user = process?.credentials?.session?.name;
@@ -462,16 +444,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 (
@@ -577,19 +558,6 @@ async function getProcessBlob(blobId, key, options) {
);
}
};
imports.ssb.setServerFollowingMe = function (id, following) {
if (
process.credentials &&
process.credentials.session &&
process.credentials.session.name
) {
return ssb.setServerFollowingMe(
process.credentials.session.name,
id,
following
);
}
};
imports.ssb.swapWithServerIdentity = function (id) {
if (
process.credentials &&
@@ -664,17 +632,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);
@@ -701,7 +678,7 @@ async function getProcessBlob(blobId, key, options) {
);
}
} catch (e) {
printError({print: print}, e);
printError(e);
}
broadcastEvent('onSessionBegin', [getUser(process, process)]);
if (process.app) {
@@ -715,14 +692,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);
}
@@ -730,6 +703,9 @@ async function getProcessBlob(blobId, key, options) {
return process;
}
/**
* SSB message added callback.
*/
ssb.addEventListener('message', function () {
broadcastEvent('onMessage', [...arguments]);
});
@@ -743,7 +719,8 @@ ssb.addEventListener('connections', function () {
});
/**
* TODOC
* Load settings from the database.
* @return The settings as a key value pairs object.
*/
async function loadSettings() {
let data = {};
@@ -764,7 +741,7 @@ async function loadSettings() {
}
/**
* TODOC
* Send periodic stats to all clients.
*/
function sendStats() {
let apps = Object.values(gProcesses)
@@ -781,8 +758,16 @@ function sendStats() {
}
}
let g_handler_index = 0;
/**
* Invoke an app's handler.js.
* @param response The response object.
* @param app_blob_id The app's blob identifier.
* @param path The request path.
* @param query The request query string.
* @param headers The request headers.
* @param package_owner The app's owner.
* @param package_name The app's name.
*/
exports.callAppHandler = async function callAppHandler(
response,
app_blob_id,
@@ -848,59 +833,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};
/** @} */

View File

@@ -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};

View File

@@ -1,8 +1,14 @@
/**
* TODOC
* TODO: document so we can improve this
* @param {*} url
* @returns
* \file
* \defgroup tfhttp Tilde Friends HTTP Client JS
* Tilde Friends server-side HTTP client.
* @{
*/
/**
* Parse a URL into protocol, host, path, and port parts.
* @param url
* @return An object of the URL parts.
*/
function parseUrl(url) {
// XXX: Hack.
@@ -16,9 +22,9 @@ function parseUrl(url) {
}
/**
* TODOC
* @param {*} data
* @returns
* Parse an HTTP response into headers and body content.
* @param data The response data, headers and body included.
* @return headers and body data.
*/
function parseResponse(data) {
let firstLine;
@@ -36,15 +42,15 @@ function parseResponse(data) {
headers[line.substring(colon)] = line.substring(colon + 1);
}
}
return {body: data};
return {headers: headers, body: data};
}
/**
* TODOC
* @param {*} url
* @param {*} options
* @param {*} allowed_hosts
* @returns
* Make an HTTP request.
* @param url The URL.
* @param options Request options.
* @param allowed_hosts List of allowed hosts.
* @return A promise resolved with the response headers and body.
*/
export function fetch(url, options, allowed_hosts) {
let parsed = parseUrl(url);
@@ -111,3 +117,5 @@ export function fetch(url, options, allowed_hosts) {
});
});
}
/** @} */

View File

@@ -38,8 +38,8 @@
style="
display: flex;
flex-flow: column;
width: 100vw;
height: 100vh;
width: 100%;
height: 100%;
position: absolute;
max-width: 100%;
max-height: 100%;

View File

@@ -7,8 +7,8 @@ html {
body {
font-family: monospace;
background-color: #002b36;
color: #eee8d5;
background-color: #444;
color: #fff;
width: 100%;
height: 100%;
padding: 0;

View File

@@ -1,11 +1,22 @@
/**
* \file
* \defgroup tfrpc Tilde Friends RPC.
* Tilde Friends RPC.
* @{
*/
/** Whether this module is being run in a web browser. */
const k_is_browser = get_is_browser();
/** Registered methods. */
let g_api = {};
/** The next method identifier. */
let g_next_id = 1;
/** Identifiers of pending calls. */
let g_calls = {};
/**
* TODOC
* @returns
* Check if being called from a browser vs. server-side.
* @return true if called from a browser.
*/
function get_is_browser() {
try {
@@ -15,16 +26,30 @@ function get_is_browser() {
}
}
/** \cond */
if (k_is_browser) {
print = console.log;
}
if (k_is_browser) {
window.addEventListener('message', function (event) {
call_rpc(event.data);
});
} else {
core.register('message', function (message) {
call_rpc(message?.message);
});
}
export let rpc = new Proxy({}, {get: make_rpc});
/** \endcond */
/**
* TODOC
* @param {*} target
* @param {*} prop
* @param {*} receiver
* @returns
* Make a function to invoke a remote procedure.
* @param target The target.
* @param prop The name of the function.
* @param receiver The receiver.
* @return A function.
*/
function make_rpc(target, prop, receiver) {
return function () {
@@ -55,8 +80,8 @@ function make_rpc(target, prop, receiver) {
}
/**
* TODOC
* @param {*} response
* Send a response.
* @param response The response.
*/
function send(response) {
if (k_is_browser) {
@@ -67,8 +92,8 @@ function send(response) {
}
/**
* TODOC
* @param {*} message
* Invoke a remote procedure.
* @param message An object describing the call.
*/
function call_rpc(message) {
if (message && message.message === 'tfrpc') {
@@ -112,22 +137,12 @@ function call_rpc(message) {
}
}
if (k_is_browser) {
window.addEventListener('message', function (event) {
call_rpc(event.data);
});
} else {
core.register('message', function (message) {
call_rpc(message?.message);
});
}
export let rpc = new Proxy({}, {get: make_rpc});
/**
* TODOC
* @param {*} method
* Register a function that to be called remotely.
* @param method The method.
*/
export function register(method) {
g_api[method.name] = method;
}
/** @} */

View File

@@ -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}

View File

@@ -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,14 +25,14 @@
}:
pkgs.stdenv.mkDerivation rec {
pname = "tildefriends";
version = "0.0.26";
version = "0.2025.8";
src = pkgs.fetchFromGitea {
domain = "dev.tildefriends.net";
owner = "cory";
repo = "tildefriends";
rev = "v${version}";
hash = "sha256-XJ7M++risfsRn9GkS1zjTQpqqV5S09uyimeVzU9hGGg=";
hash = "sha256-N/5lp8RL19B6Z43kRxx7c01WVJkK44a/wwNgRJPk5uI=";
fetchSubmodules = true;
};
@@ -46,7 +50,7 @@ pkgs.stdenv.mkDerivation rec {
];
buildPhase = ''
make -j $NIX_BUILD_CORES release
make -j $NIX_BUILD_CORES release USE_SYSTEM_SSL=1
'';
installPhase = ''

2
deps/c-ares vendored

File diff suppressed because one or more lines are too long

349
deps/codemirror_src/package-lock.json generated vendored
View File

@@ -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.7",
"resolved": "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.18.7.tgz",
"integrity": "sha512-8EzdeIoWPJDsMBwz3zdzwXnUpCzMiCyz5/A3FIPpriaclFCGDkAzK13sMcnsu5rowqiyeQN2Vs2TsOcoDPZirQ==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
@@ -31,9 +31,9 @@
}
},
"node_modules/@codemirror/commands": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.7.1.tgz",
"integrity": "sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==",
"version": "6.8.1",
"resolved": "https://registry.npmjs.org/@codemirror/commands/-/commands-6.8.1.tgz",
"integrity": "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
@@ -73,9 +73,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==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
@@ -88,9 +88,9 @@
}
},
"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==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
@@ -98,9 +98,9 @@
}
},
"node_modules/@codemirror/language": {
"version": "6.10.7",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.10.7.tgz",
"integrity": "sha512-aOswhVOLYhMNeqykt4P7+ukQSpGL0ynZYaEyFDVHE7fl2xgluU3yuE9MdgYNfw6EmaNidoFMIQ2iTh1ADrnT6A==",
"version": "6.11.3",
"resolved": "https://registry.npmjs.org/@codemirror/language/-/language-6.11.3.tgz",
"integrity": "sha512-9HBM2XnwDj7fnu0551HkGdrUrrqmYq/WC5iv6nbY2WdicXdGbhR/gfbZOH73Aqj4351alY1+aoG9rCNfiwS1RA==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
@@ -112,9 +112,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==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
@@ -123,9 +123,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==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.0.0",
@@ -134,18 +134,18 @@
}
},
"node_modules/@codemirror/state": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.0.tgz",
"integrity": "sha512-MwBHVK60IiIHDcoMet78lxt6iw5gJOGSbNbOIVBHWVXIH4/Nq1+GQgLLGgI1KlnN86WDXsPudVaqYHKBIx7Eyw==",
"version": "6.5.2",
"resolved": "https://registry.npmjs.org/@codemirror/state/-/state-6.5.2.tgz",
"integrity": "sha512-FVqsPqtPWKVVL3dPSxy8wEF/ymIEuVzF1PK3VbUgrxXpJUSHQWWZz4JMToquRxnkw+36LTamCZG2iua2Ptq0fA==",
"license": "MIT",
"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==",
"license": "MIT",
"dependencies": {
"@codemirror/language": "^6.0.0",
@@ -155,29 +155,26 @@
}
},
"node_modules/@codemirror/view": {
"version": "6.36.1",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.36.1.tgz",
"integrity": "sha512-miD1nyT4m4uopZaDdO2uXU/LLHliKNYL9kB1C1wJHrunHLm/rpkb5QVSokqgw9hFqEZakrdlb/VGWX8aYZTslQ==",
"version": "6.38.2",
"resolved": "https://registry.npmjs.org/@codemirror/view/-/view-6.38.2.tgz",
"integrity": "sha512-bTWAJxL6EOFLPzTx+O5P5xAO3gTqpatQ2b/ARQ8itfU/v2LlpS3pH2fkL0A3E/Fx8Y2St2KES7ZEV0sHTsSW/A==",
"license": "MIT",
"dependencies": {
"@codemirror/state": "^6.5.0",
"crelt": "^1.0.6",
"style-mod": "^4.1.0",
"w3c-keyname": "^2.2.4"
}
},
"node_modules/@jridgewell/gen-mapping": {
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
"integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
"version": "0.3.13",
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
"integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/set-array": "^1.2.1",
"@jridgewell/sourcemap-codec": "^1.4.10",
"@jridgewell/sourcemap-codec": "^1.5.0",
"@jridgewell/trace-mapping": "^0.3.24"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
@@ -190,20 +187,10 @@
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/set-array": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
"integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/source-map": {
"version": "0.3.6",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz",
"integrity": "sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==",
"version": "0.3.11",
"resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
"integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -212,16 +199,16 @@
}
},
"node_modules/@jridgewell/sourcemap-codec": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
"integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
"version": "0.3.25",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
"integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
"version": "0.3.30",
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.30.tgz",
"integrity": "sha512-GQ7Nw5G2lTu/BtHTKfXhKHok2WGetd4XYcVKGx00SjAk8GMwgJM3zr6zORiPGuOE+/vkc90KtTosSSvaCjKb2Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -236,14 +223,14 @@
"license": "MIT"
},
"node_modules/@lezer/css": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.1.9.tgz",
"integrity": "sha512-TYwgljcDv+YrV0MZFFvYFQHCfGgbPMR6nuqLabBdmZoFH3EP1gvw8t0vae326Ne3PszQkbXfVBjCnf3ZVCr0bA==",
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@lezer/css/-/css-1.3.0.tgz",
"integrity": "sha512-pBL7hup88KbI7hXnZV3PQsn43DHy6TWyzuyk2AO9UyoXcDltvIdqWKE1dLL/45JVZ+YZkHe1WVHqO6wugZZWcw==",
"license": "MIT",
"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": {
@@ -267,9 +254,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==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
@@ -278,9 +265,9 @@
}
},
"node_modules/@lezer/json": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.2.tgz",
"integrity": "sha512-xHT2P4S5eeCYECyKNPhr4cbEL9tc8w83SPwRC373o9uEdrvGKTZoJVAGxpOsZckMlEh9W23Pc72ew918RWQOBQ==",
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/@lezer/json/-/json-1.0.3.tgz",
"integrity": "sha512-BP9KzdF9Y35PDpv04r0VeSTKDeox5vVr3efE7eBbx3r4s3oNLfunchejZhjArmeieBH+nVOpgIiBJpEAv8ilqQ==",
"license": "MIT",
"dependencies": {
"@lezer/common": "^1.2.0",
@@ -351,9 +338,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==",
"license": "MIT",
"dependencies": {
"@types/estree": "^1.0.0",
@@ -373,9 +360,9 @@
}
},
"node_modules/@rollup/rollup-android-arm-eabi": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.29.1.tgz",
"integrity": "sha512-ssKhA8RNltTZLpG6/QNkCSge+7mBQGUqJRisZ2MDQcEGaK93QESEgWK2iOpIDZ7k9zPVkG5AS3ksvD5ZWxmItw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.50.0.tgz",
"integrity": "sha512-lVgpeQyy4fWN5QYebtW4buT/4kn4p4IJ+kDNB4uYNT5b8c8DLJDg6titg20NIg7E8RWwdWZORW6vUFfrLyG3KQ==",
"cpu": [
"arm"
],
@@ -386,9 +373,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.29.1.tgz",
"integrity": "sha512-CaRfrV0cd+NIIcVVN/jx+hVLN+VRqnuzLRmfmlzpOzB87ajixsN/+9L5xNmkaUUvEbI5BmIKS+XTwXsHEb65Ew==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.50.0.tgz",
"integrity": "sha512-2O73dR4Dc9bp+wSYhviP6sDziurB5/HCym7xILKifWdE9UsOe2FtNcM+I4xZjKrfLJnq5UR8k9riB87gauiQtw==",
"cpu": [
"arm64"
],
@@ -399,9 +386,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.29.1.tgz",
"integrity": "sha512-2ORr7T31Y0Mnk6qNuwtyNmy14MunTAMx06VAPI6/Ju52W10zk1i7i5U3vlDRWjhOI5quBcrvhkCHyF76bI7kEw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.50.0.tgz",
"integrity": "sha512-vwSXQN8T4sKf1RHr1F0s98Pf8UPz7pS6P3LG9NSmuw0TVh7EmaE+5Ny7hJOZ0M2yuTctEsHHRTMi2wuHkdS6Hg==",
"cpu": [
"arm64"
],
@@ -412,9 +399,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.29.1.tgz",
"integrity": "sha512-j/Ej1oanzPjmN0tirRd5K2/nncAhS9W6ICzgxV+9Y5ZsP0hiGhHJXZ2JQ53iSSjj8m6cRY6oB1GMzNn2EUt6Ng==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.50.0.tgz",
"integrity": "sha512-cQp/WG8HE7BCGyFVuzUg0FNmupxC+EPZEwWu2FCGGw5WDT1o2/YlENbm5e9SMvfDFR6FRhVCBePLqj0o8MN7Vw==",
"cpu": [
"x64"
],
@@ -425,9 +412,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.29.1.tgz",
"integrity": "sha512-91C//G6Dm/cv724tpt7nTyP+JdN12iqeXGFM1SqnljCmi5yTXriH7B1r8AD9dAZByHpKAumqP1Qy2vVNIdLZqw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.50.0.tgz",
"integrity": "sha512-UR1uTJFU/p801DvvBbtDD7z9mQL8J80xB0bR7DqW7UGQHRm/OaKzp4is7sQSdbt2pjjSS72eAtRh43hNduTnnQ==",
"cpu": [
"arm64"
],
@@ -438,9 +425,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.29.1.tgz",
"integrity": "sha512-hEioiEQ9Dec2nIRoeHUP6hr1PSkXzQaCUyqBDQ9I9ik4gCXQZjJMIVzoNLBRGet+hIUb3CISMh9KXuCcWVW/8w==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.50.0.tgz",
"integrity": "sha512-G/DKyS6PK0dD0+VEzH/6n/hWDNPDZSMBmqsElWnCRGrYOb2jC0VSupp7UAHHQ4+QILwkxSMaYIbQ72dktp8pKA==",
"cpu": [
"x64"
],
@@ -451,9 +438,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.29.1.tgz",
"integrity": "sha512-Py5vFd5HWYN9zxBv3WMrLAXY3yYJ6Q/aVERoeUFwiDGiMOWsMs7FokXihSOaT/PMWUty/Pj60XDQndK3eAfE6A==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.50.0.tgz",
"integrity": "sha512-u72Mzc6jyJwKjJbZZcIYmd9bumJu7KNmHYdue43vT1rXPm2rITwmPWF0mmPzLm9/vJWxIRbao/jrQmxTO0Sm9w==",
"cpu": [
"arm"
],
@@ -464,9 +451,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.29.1.tgz",
"integrity": "sha512-RiWpGgbayf7LUcuSNIbahr0ys2YnEERD4gYdISA06wa0i8RALrnzflh9Wxii7zQJEB2/Eh74dX4y/sHKLWp5uQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.50.0.tgz",
"integrity": "sha512-S4UefYdV0tnynDJV1mdkNawp0E5Qm2MtSs330IyHgaccOFrwqsvgigUD29uT+B/70PDY1eQ3t40+xf6wIvXJyg==",
"cpu": [
"arm"
],
@@ -477,9 +464,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.29.1.tgz",
"integrity": "sha512-Z80O+taYxTQITWMjm/YqNoe9d10OX6kDh8X5/rFCMuPqsKsSyDilvfg+vd3iXIqtfmp+cnfL1UrYirkaF8SBZA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.50.0.tgz",
"integrity": "sha512-1EhkSvUQXJsIhk4msxP5nNAUWoB4MFDHhtc4gAYvnqoHlaL9V3F37pNHabndawsfy/Tp7BPiy/aSa6XBYbaD1g==",
"cpu": [
"arm64"
],
@@ -490,9 +477,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.29.1.tgz",
"integrity": "sha512-fOHRtF9gahwJk3QVp01a/GqS4hBEZCV1oKglVVq13kcK3NeVlS4BwIFzOHDbmKzt3i0OuHG4zfRP0YoG5OF/rA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.50.0.tgz",
"integrity": "sha512-EtBDIZuDtVg75xIPIK1l5vCXNNCIRM0OBPUG+tbApDuJAy9mKago6QxX+tfMzbCI6tXEhMuZuN1+CU8iDW+0UQ==",
"cpu": [
"arm64"
],
@@ -503,9 +490,9 @@
]
},
"node_modules/@rollup/rollup-linux-loongarch64-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.29.1.tgz",
"integrity": "sha512-5a7q3tnlbcg0OodyxcAdrrCxFi0DgXJSoOuidFUzHZ2GixZXQs6Tc3CHmlvqKAmOs5eRde+JJxeIf9DonkmYkw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.50.0.tgz",
"integrity": "sha512-BGYSwJdMP0hT5CCmljuSNx7+k+0upweM2M4YGfFBjnFSZMHOLYR0gEEj/dxyYJ6Zc6AiSeaBY8dWOa11GF/ppQ==",
"cpu": [
"loong64"
],
@@ -515,10 +502,10 @@
"linux"
]
},
"node_modules/@rollup/rollup-linux-powerpc64le-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.29.1.tgz",
"integrity": "sha512-9b4Mg5Yfz6mRnlSPIdROcfw1BU22FQxmfjlp/CShWwO3LilKQuMISMTtAu/bxmmrE6A902W2cZJuzx8+gJ8e9w==",
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.50.0.tgz",
"integrity": "sha512-I1gSMzkVe1KzAxKAroCJL30hA4DqSi+wGc5gviD0y3IL/VkvcnAqwBf4RHXHyvH66YVHxpKO8ojrgc4SrWAnLg==",
"cpu": [
"ppc64"
],
@@ -529,9 +516,22 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.29.1.tgz",
"integrity": "sha512-G5pn0NChlbRM8OJWpJFMX4/i8OEU538uiSv0P6roZcbpe/WfhEO+AT8SHVKfp8qhDQzaz7Q+1/ixMy7hBRidnQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.50.0.tgz",
"integrity": "sha512-bSbWlY3jZo7molh4tc5dKfeSxkqnf48UsLqYbUhnkdnfgZjgufLS/NTA8PcP/dnvct5CCdNkABJ56CbclMRYCA==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.50.0.tgz",
"integrity": "sha512-LSXSGumSURzEQLT2e4sFqFOv3LWZsEF8FK7AAv9zHZNDdMnUPYH3t8ZlaeYYZyTXnsob3htwTKeWtBIkPV27iQ==",
"cpu": [
"riscv64"
],
@@ -542,9 +542,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.29.1.tgz",
"integrity": "sha512-WM9lIkNdkhVwiArmLxFXpWndFGuOka4oJOZh8EP3Vb8q5lzdSCBuhjavJsw68Q9AKDGeOOIHYzYm4ZFvmWez5g==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.50.0.tgz",
"integrity": "sha512-CxRKyakfDrsLXiCyucVfVWVoaPA4oFSpPpDwlMcDFQvrv3XY6KEzMtMZrA+e/goC8xxp2WSOxHQubP8fPmmjOQ==",
"cpu": [
"s390x"
],
@@ -555,9 +555,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.29.1.tgz",
"integrity": "sha512-87xYCwb0cPGZFoGiErT1eDcssByaLX4fc0z2nRM6eMtV9njAfEE6OW3UniAoDhX4Iq5xQVpE6qO9aJbCFumKYQ==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.50.0.tgz",
"integrity": "sha512-8PrJJA7/VU8ToHVEPu14FzuSAqVKyo5gg/J8xUerMbyNkWkO9j2ExBho/68RnJsMGNJq4zH114iAttgm7BZVkA==",
"cpu": [
"x64"
],
@@ -568,9 +568,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.29.1.tgz",
"integrity": "sha512-xufkSNppNOdVRCEC4WKvlR1FBDyqCSCpQeMMgv9ZyXqqtKBfkw1yfGMTUTs9Qsl6WQbJnsGboWCp7pJGkeMhKA==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.50.0.tgz",
"integrity": "sha512-SkE6YQp+CzpyOrbw7Oc4MgXFvTw2UIBElvAvLCo230pyxOLmYwRPwZ/L5lBe/VW/qT1ZgND9wJfOsdy0XptRvw==",
"cpu": [
"x64"
],
@@ -580,10 +580,23 @@
"linux"
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.50.0.tgz",
"integrity": "sha512-PZkNLPfvXeIOgJWA804zjSFH7fARBBCpCXxgkGDRjjAhRLOR8o0IGS01ykh5GYfod4c2yiiREuDM8iZ+pVsT+Q==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"openharmony"
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.29.1.tgz",
"integrity": "sha512-F2OiJ42m77lSkizZQLuC+jiZ2cgueWQL5YC9tjo3AgaEw+KJmVxHGSyQfDUoYR9cci0lAywv2Clmckzulcq6ig==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.50.0.tgz",
"integrity": "sha512-q7cIIdFvWQoaCbLDUyUc8YfR3Jh2xx3unO8Dn6/TTogKjfwrax9SyfmGGK6cQhKtjePI7jRfd7iRYcxYs93esg==",
"cpu": [
"arm64"
],
@@ -594,9 +607,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.29.1.tgz",
"integrity": "sha512-rYRe5S0FcjlOBZQHgbTKNrqxCBUmgDJem/VQTCcTnA2KCabYSWQDrytOzX7avb79cAAweNmMUb/Zw18RNd4mng==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.50.0.tgz",
"integrity": "sha512-XzNOVg/YnDOmFdDKcxxK410PrcbcqZkBmz+0FicpW5jtjKQxcW1BZJEQOF0NJa6JO7CZhett8GEtRN/wYLYJuw==",
"cpu": [
"ia32"
],
@@ -607,9 +620,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.29.1.tgz",
"integrity": "sha512-+10CMg9vt1MoHj6x1pxyjPSMjHTIlqs8/tBztXvPAx24SKs9jwVnKqHJumlH/IzhaPUaj3T6T6wfZr8okdXaIg==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.50.0.tgz",
"integrity": "sha512-xMmiWRR8sp72Zqwjgtf3QbZfF1wdh8X2ABu3EaozvZcyHJeU0r+XAnXdKgs4cCAp6ORoYoCygipYP1mjmbjrsg==",
"cpu": [
"x64"
],
@@ -620,9 +633,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==",
"license": "MIT"
},
"node_modules/@types/resolve": {
@@ -632,9 +645,9 @@
"license": "MIT"
},
"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,
"license": "MIT",
"bin": {
@@ -652,9 +665,9 @@
"license": "MIT"
},
"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==",
"license": "MIT",
"dependencies": {
"@codemirror/autocomplete": "^6.0.0",
@@ -757,9 +770,9 @@
"license": "MIT"
},
"node_modules/picomatch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
"integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"license": "MIT",
"engines": {
"node": ">=12"
@@ -799,12 +812,12 @@
}
},
"node_modules/rollup": {
"version": "4.29.1",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.29.1.tgz",
"integrity": "sha512-RaJ45M/kmJUzSWDs1Nnd5DdV4eerC98idtUOVr6FfKcgxqvjwHmxc5upLF9qZU9EpsVzzhleFahrT3shLuJzIw==",
"version": "4.50.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.50.0.tgz",
"integrity": "sha512-/Zl4D8zPifNmyGzJS+3kVoyXeDeT/GrsJM94sACNg9RtUE0hrHa1bNPtRSrfHTMH5HjRzce6K7rlTh3Khiw+pw==",
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.6"
"@types/estree": "1.0.8"
},
"bin": {
"rollup": "dist/bin/rollup"
@@ -814,25 +827,27 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
"@rollup/rollup-android-arm-eabi": "4.29.1",
"@rollup/rollup-android-arm64": "4.29.1",
"@rollup/rollup-darwin-arm64": "4.29.1",
"@rollup/rollup-darwin-x64": "4.29.1",
"@rollup/rollup-freebsd-arm64": "4.29.1",
"@rollup/rollup-freebsd-x64": "4.29.1",
"@rollup/rollup-linux-arm-gnueabihf": "4.29.1",
"@rollup/rollup-linux-arm-musleabihf": "4.29.1",
"@rollup/rollup-linux-arm64-gnu": "4.29.1",
"@rollup/rollup-linux-arm64-musl": "4.29.1",
"@rollup/rollup-linux-loongarch64-gnu": "4.29.1",
"@rollup/rollup-linux-powerpc64le-gnu": "4.29.1",
"@rollup/rollup-linux-riscv64-gnu": "4.29.1",
"@rollup/rollup-linux-s390x-gnu": "4.29.1",
"@rollup/rollup-linux-x64-gnu": "4.29.1",
"@rollup/rollup-linux-x64-musl": "4.29.1",
"@rollup/rollup-win32-arm64-msvc": "4.29.1",
"@rollup/rollup-win32-ia32-msvc": "4.29.1",
"@rollup/rollup-win32-x64-msvc": "4.29.1",
"@rollup/rollup-android-arm-eabi": "4.50.0",
"@rollup/rollup-android-arm64": "4.50.0",
"@rollup/rollup-darwin-arm64": "4.50.0",
"@rollup/rollup-darwin-x64": "4.50.0",
"@rollup/rollup-freebsd-arm64": "4.50.0",
"@rollup/rollup-freebsd-x64": "4.50.0",
"@rollup/rollup-linux-arm-gnueabihf": "4.50.0",
"@rollup/rollup-linux-arm-musleabihf": "4.50.0",
"@rollup/rollup-linux-arm64-gnu": "4.50.0",
"@rollup/rollup-linux-arm64-musl": "4.50.0",
"@rollup/rollup-linux-loongarch64-gnu": "4.50.0",
"@rollup/rollup-linux-ppc64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-gnu": "4.50.0",
"@rollup/rollup-linux-riscv64-musl": "4.50.0",
"@rollup/rollup-linux-s390x-gnu": "4.50.0",
"@rollup/rollup-linux-x64-gnu": "4.50.0",
"@rollup/rollup-linux-x64-musl": "4.50.0",
"@rollup/rollup-openharmony-arm64": "4.50.0",
"@rollup/rollup-win32-arm64-msvc": "4.50.0",
"@rollup/rollup-win32-ia32-msvc": "4.50.0",
"@rollup/rollup-win32-x64-msvc": "4.50.0",
"fsevents": "~2.3.2"
}
},
@@ -914,14 +929,14 @@
}
},
"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.44.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz",
"integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==",
"dev": true,
"license": "BSD-2-Clause",
"dependencies": {
"@jridgewell/source-map": "^0.3.3",
"acorn": "^8.8.2",
"acorn": "^8.15.0",
"commander": "^2.20.0",
"source-map-support": "~0.5.20"
},

2
deps/libuv vendored

Submodule deps/libuv updated: e1095c7a43...5152db2cbf

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
deps/quickjs vendored

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,2 @@
(()=>{var D="./favicon-16x16-VSI62OPJ.png";})();
//# sourceMappingURL=favicon-16x16-V2DMIAZS.js.map

View File

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 679 B

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -0,0 +1,2 @@
(()=>{var T="./favicon-32x32-3EB2YCUY.png";})();
//# sourceMappingURL=favicon-32x32-THY3JDJL.js.map

BIN
deps/speedscope/favicon-FOKUP5Y5.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

2
deps/speedscope/favicon-M34RF7BI.js vendored Normal file
View File

@@ -0,0 +1,2 @@
(()=>{var m="./favicon-FOKUP5Y5.ico";})();
//# sourceMappingURL=favicon-M34RF7BI.js.map

File diff suppressed because one or more lines are too long

View File

@@ -1,2 +1,19 @@
<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>speedscope</title><link href="source-code-pro.52b1676f.css" rel="stylesheet"><script></script><link rel="stylesheet" href="reset.8c46b7a1.css"><link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32.bc503437.png"><link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16.f74b3187.png"></head><body> <script src="speedscope.6f107512.js"></script>
</body></html>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>speedscope</title>
<link rel="stylesheet" href="speedscope-GHPHNKXC.css">
<link rel="icon" type="image/png" sizes="32x32" href="favicon-32x32-3EB2YCUY.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon-16x16-VSI62OPJ.png">
<link rel="icon" type="image/x-icon" href="favicon-FOKUP5Y5.ico">
</head>
<body>
<script src="speedscope-HCR63FMT.js"></script>
</body>
</html>

View File

@@ -1,3 +1,3 @@
speedscope@1.21.0
Sat Nov 16 22:13:27 PST 2024
d36c3a54424063a8df7bc67a7b824a223d73861b
speedscope@1.23.1
Mon Aug 11 11:43:09 PDT 2025
0cec0f82c334aed6cf19d43cabeadcda0d95e0fc

View File

@@ -1,2 +0,0 @@
a,abbr,acronym,address,applet,article,aside,audio,b,big,blockquote,body,canvas,caption,center,cite,code,dd,del,details,dfn,div,dl,dt,em,embed,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,output,p,pre,q,ruby,s,samp,section,small,span,strike,strong,sub,summary,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,u,ul,var,video{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:initial}article,aside,details,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:after,blockquote:before,q:after,q:before{content:"";content:none}table{border-collapse:collapse;border-spacing:0}html{overflow:hidden}body,html{height:100%}body{overflow:auto}
/*# sourceMappingURL=reset.8c46b7a1.css.map */

View File

@@ -1,2 +0,0 @@
@font-face{font-family:Source Code Pro;font-weight:400;font-style:normal;font-stretch:normal;src:url(SourceCodePro-Regular.ttf.f546cbe0.woff2) format("woff2")}
/*# sourceMappingURL=source-code-pro.52b1676f.css.map */

Some files were not shown because too many files have changed in this diff Show More