docs: Expose the rest of the core js to doxygen.
All checks were successful
Build Tilde Friends / Build-All (push) Successful in 31m35s

This commit is contained in:
2025-07-27 21:48:18 -04:00
parent e4729b22f2
commit 705e8b553f
4 changed files with 248 additions and 227 deletions

View File

@@ -945,13 +945,13 @@ WARN_LOGFILE =
INPUT = README.md \
core/app.js \
core/client.js \
core/core.js \
core/http.js \
core/tfrpc.js \
docs/ \
src/
# Not yet: core/tfrpc.js core/client.js
# 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

View File

@@ -1,3 +1,11 @@
/**
* \file
* \defgroup tfclient Tilde Friends Client JS
* Tilde Friends client-side browser JavaScript.
* @{
*/
/** \cond */
import {LitElement, html, css, svg} from '/lit/lit-all.min.js';
let cm6;
@@ -13,8 +21,9 @@ let gUnloading;
let kErrorColor = '#dc322f';
let kDisconnectColor = '#f00';
let kStatusColor = '#fff';
/** \endcond */
// Functions that server-side app code can call through the app object.
/** Functions that server-side app code can call through the app object. */
const k_api = {
setDocument: {args: ['content'], func: api_setDocument},
postMessage: {args: ['message'], func: api_postMessage},
@@ -26,29 +35,14 @@ const k_api = {
setHash: {args: ['hash'], func: api_setHash},
};
// TODO(tasiaiso): this is only used once, move it down ?
const k_global_style = css`
a:link {
color: #268bd2;
}
a:visited {
color: #6c71c4;
}
a:hover {
color: #859900;
}
a:active {
color: #2aa198;
}
`;
/**
* Class that represents the top bar
*/
class TfNavigationElement extends LitElement {
/**
* Get Lit Html properties.
* @return The properties.
*/
static get properties() {
return {
credentials: {type: Object},
@@ -64,6 +58,9 @@ class TfNavigationElement extends LitElement {
};
}
/**
* Create a TfNavigationElement instance.
*/
constructor() {
super();
this.permissions = {};
@@ -76,7 +73,7 @@ class TfNavigationElement extends LitElement {
/**
* TODOC
* @param {*} event
* @param event The HTML event.
*/
toggle_edit(event) {
event.preventDefault();
@@ -89,7 +86,7 @@ class TfNavigationElement extends LitElement {
/**
* TODOC
* @param {*} key
* @param key The permission to reset.
*/
reset_permission(key) {
send({action: 'resetPermission', permission: key});
@@ -97,9 +94,9 @@ class TfNavigationElement extends LitElement {
/**
* TODOC
* @param {*} key
* @param {*} options
* @returns
* @param key The spark line identifier.
* @param options Spark line options.
* @return A spark line HTML element.
*/
get_spark_line(key, options) {
if (!this.spark_lines[key]) {
@@ -118,29 +115,49 @@ class TfNavigationElement extends LitElement {
return this.spark_lines[key];
}
/**
* Set the active SSB identity for the current application.
* @param id The identity.
*/
set_active_identity(id) {
send({action: 'setActiveIdentity', identity: id});
this.renderRoot.getElementById('id_dropdown').classList.remove('w3-show');
}
create_identity(event) {
/**
* Create a new SSB identity.
*/
create_identity() {
if (confirm('Are you sure you want to create a new identity?')) {
send({action: 'createIdentity'});
}
}
/**
* Toggle visibility of the ID dropdown.
*/
toggle_id_dropdown() {
this.renderRoot.getElementById('id_dropdown').classList.toggle('w3-show');
}
/**
* Edit the current identity's SSB profile.
*/
edit_profile() {
window.location.href = '/~core/ssb/#' + this.identity;
}
/**
* Sign out of the current Tilde Friends user.
*/
logout() {
window.location.href = `/login/logout?return=${encodeURIComponent(url() + hash())}`;
}
/**
* Render the identity dropdown.
* @return Lit HTML.
*/
render_identity() {
let self = this;
@@ -287,6 +304,9 @@ class TfNavigationElement extends LitElement {
}
}
/**
* Clear the current error.
*/
clear_error() {
this.status = {};
}
@@ -297,6 +317,23 @@ class TfNavigationElement extends LitElement {
*/
render() {
let self = this;
const k_global_style = css`
a:link {
color: #268bd2;
}
a:visited {
color: #6c71c4;
}
a:hover {
color: #859900;
}
a:active {
color: #2aa198;
}
`;
return html`
<link type="text/css" rel="stylesheet" href="/static/w3.css" />
<style>
@@ -407,6 +444,10 @@ customElements.define('tf-navigation', TfNavigationElement);
* TODOC
*/
class TfFilesElement extends LitElement {
/**
* LitElement properties.
* @return The properties.
*/
static get properties() {
return {
current: {type: String},
@@ -416,6 +457,9 @@ class TfFilesElement extends LitElement {
};
}
/**
* Create a TfFilesElement instance.
*/
constructor() {
super();
this.files = {};
@@ -424,7 +468,7 @@ class TfFilesElement extends LitElement {
/**
* TODOC
* @param {*} file
* @param file The file.
*/
file_click(file) {
this.dispatchEvent(
@@ -440,8 +484,8 @@ class TfFilesElement extends LitElement {
/**
* TODOC
* @param {*} file
* @returns
* @param file The file.
* @returns Lit HTML.
*/
render_file(file) {
let classes = ['file'];
@@ -464,7 +508,7 @@ class TfFilesElement extends LitElement {
/**
* TODOC
* @param {*} event
* @param event The event.
*/
async drop(event) {
event.preventDefault();
@@ -490,7 +534,7 @@ class TfFilesElement extends LitElement {
/**
* TODOC
* @param {*} event
* @param event The event.
*/
drag_enter(event) {
this.dropping++;
@@ -500,7 +544,7 @@ class TfFilesElement extends LitElement {
/**
* TODOC
* @param {*} event
* @param event The event.
*/
drag_leave(event) {
this.dropping--;
@@ -509,6 +553,10 @@ class TfFilesElement extends LitElement {
}
}
/**
* Drag over event.
* @param event The event.
*/
drag_over(event) {
event.preventDefault();
}
@@ -565,6 +613,10 @@ customElements.define('tf-files', TfFilesElement);
* TODOC
*/
class TfFilesPaneElement extends LitElement {
/**
* Get Lit Html properties.
* @return The properties.
*/
static get properties() {
return {
expanded: {type: Boolean},
@@ -573,6 +625,9 @@ class TfFilesPaneElement extends LitElement {
};
}
/**
* Create a TfFilesPaneElement instance.
*/
constructor() {
super();
this.expanded = window.localStorage.getItem('files') != '0';
@@ -581,7 +636,7 @@ class TfFilesPaneElement extends LitElement {
/**
* TODOC
* @param {*} expanded
* @param expanded Whether the files pane is expanded.
*/
set_expanded(expanded) {
this.expanded = expanded;
@@ -760,10 +815,9 @@ window.addEventListener('keydown', function (event) {
});
/**
* TODOC
* @param {*} nodes
* @param {*} callback
* @returns
* Make sure a set of dependencies are loaded
* @param nodes An array of descriptions of dependencies to load.
* @param callback Called when all dependencies are loaded.
*/
function ensureLoaded(nodes, callback) {
if (!nodes.length) {
@@ -857,23 +911,10 @@ function trace() {
}
/**
* TODOC
* @param {*} name
* @returns
*/
function guessMode(name) {
return name.endsWith('.js')
? 'javascript'
: name.endsWith('.html')
? 'htmlmixed'
: null;
}
/**
* TODOC
* @param {*} name
* @param {*} id
* @returns
* Load a single file.
* @param name The name by which the file is known.
* @param id The file's ID.
* @return A promise resolved with the file's contents.
*/
function loadFile(name, id) {
return fetch('/' + id + '/view')
@@ -899,9 +940,9 @@ function loadFile(name, id) {
}
/**
* TODOC
* @param {*} path
* @returns
* Load files for the app.
* @param path The app path to load.
* @return A promise resolved when the app is laoded.
*/
async function load(path) {
let response = await fetch((path || url()) + 'view');
@@ -958,9 +999,9 @@ function explodePath() {
}
/**
* TODOC
* @param {*} save_to
* @returns
* Save the app.
* @param save_to An optional path to which to save the app.
* @return A promise resoled when the app is saved.
*/
function save(save_to) {
document.getElementById('save').disabled = true;
@@ -1129,8 +1170,8 @@ function hash() {
}
/**
* TODOC
* @param {*} content
* Set the iframe document contents.
* @param content The contents.
*/
function api_setDocument(content) {
let iframe = document.getElementById('document');
@@ -1138,8 +1179,8 @@ function api_setDocument(content) {
}
/**
* TODOC
* @param {*} message
* Send a message to the sandboxed iframe.
* @param message The message.
*/
function api_postMessage(message) {
let iframe = document.getElementById('document');
@@ -1148,7 +1189,7 @@ function api_postMessage(message) {
/**
* TODOC
* @param {*} error
* @param error The error.
*/
function api_error(error) {
if (error) {
@@ -1162,28 +1203,28 @@ function api_error(error) {
}
/**
* TODOC
* @param {*} key
* @param {*} value
et a value in local storage.
* @param key The key.
* @param value The value.
*/
function api_localStorageSet(key, value) {
window.localStorage.setItem('app:' + key, value);
}
/**
* TODOC
* @param {*} key
* @returns
* Get a value from local storage.
* @param key The key.
* @return The value.
*/
function api_localStorageGet(key) {
return window.localStorage.getItem('app:' + key);
}
/**
* TODOC
* @param {*} permission
* @param {*} id
* @returns
* Request a permission
* @param permission The permission to request.
* @param id The id requeesting the permission.
* @return A promise fulfilled if the permission was granted.
*/
function api_requestPermission(permission, id) {
let outer = document.createElement('div');
@@ -1259,8 +1300,8 @@ function api_print() {
}
/**
* TODOC
* @param {*} hash
* Set the window's location hash.
* @param hash The new hash.
*/
function api_setHash(hash) {
window.location.hash = hash;
@@ -1268,7 +1309,7 @@ function api_setHash(hash) {
/**
* TODOC
* @param {*} message
* @param message The message.
*/
function _receive_websocket_message(message) {
if (message && message.action == 'session') {
@@ -1364,9 +1405,9 @@ function _receive_websocket_message(message) {
}
/**
* TODOC
* @param {*} message
* @param {*} color
* Set the status message.
* @param message The message.
* @param color The message's color.
*/
function setStatusMessage(message, color) {
document.getElementsByTagName('tf-navigation')[0].status = {
@@ -1377,8 +1418,8 @@ function setStatusMessage(message, color) {
}
/**
* TODOC
* @param {*} value
* Send a message to the app.
* @param value The message.
*/
function send(value) {
try {
@@ -1390,49 +1431,6 @@ function send(value) {
}
}
/**
* TODOC
* @param {*} sourceData
* @param {*} maxWidth
* @param {*} maxHeight
* @param {*} callback
*/
function fixImage(sourceData, maxWidth, maxHeight, callback) {
let result = sourceData;
let image = new Image();
image.crossOrigin = 'anonymous';
image.referrerPolicy = 'no-referrer';
image.onload = function () {
if (image.width > maxWidth || image.height > maxHeight) {
let downScale = Math.min(
maxWidth / image.width,
maxHeight / image.height
);
let canvas = document.createElement('canvas');
canvas.width = image.width * downScale;
canvas.height = image.height * downScale;
let context = canvas.getContext('2d');
context.clearRect(0, 0, canvas.width, canvas.height);
image.width = canvas.width;
image.height = canvas.height;
context.drawImage(image, 0, 0, image.width, image.height);
result = canvas.toDataURL();
}
callback(result);
};
image.src = sourceData;
}
/**
* TODOC
* @param {*} image
*/
function sendImage(image) {
fixImage(image, 320, 240, function (result) {
send({image: result});
});
}
/**
* TODOC
*/
@@ -1461,8 +1459,8 @@ function blur() {
}
/**
* TODOC
* @param {*} event
* Handle a message.
* @param event The message.
*/
function message(event) {
if (
@@ -1510,8 +1508,8 @@ function message(event) {
}
/**
* TODOC
* @param {*} path
* Reconnect the WebSocket.
* @param path The path to which the WebSocket should be connected.
*/
function reconnect(path) {
let oldSocket = gSocket;
@@ -1526,8 +1524,8 @@ function reconnect(path) {
}
/**
* TODOC
* @param {*} path
* Connect the WebSocket.
* @param path The path to which to connect.
*/
function connectSocket(path) {
if (!gSocket || gSocket.readyState != gSocket.OPEN) {
@@ -1593,8 +1591,8 @@ function connectSocket(path) {
}
/**
* TODOC
* @param {*} name
* Open a file by name.
* @param name The file to open.
*/
function openFile(name) {
let newDoc =
@@ -1641,8 +1639,8 @@ function updateFiles() {
}
/**
* TODOC
* @param {*} name
* Create a new file with the given name.
* @param name The file's name.
*/
function makeNewFile(name) {
gFiles[name] = {
@@ -1703,10 +1701,10 @@ async function appExport() {
}
/**
* TODOC
* @param {*} name
* @param {*} file
* @returns
* Save a file.
* @param name The file to svae.
* @param file The file contents.
* @return A promise resolved with the blob ID of the saved file.
*/
async function save_file_to_blob_id(name, file) {
console.log(`Saving ${name}.`);
@@ -1801,7 +1799,7 @@ async function appImport() {
}
/**
*
* Prettify the current source file.
*/
async function sourcePretty() {
let prettier = (await import('/prettier/standalone.mjs')).default;
@@ -1829,6 +1827,9 @@ async function sourcePretty() {
}
}
/**
* Toggle visible whitespace.
*/
function toggleVisibleWhitespace() {
let editor_style = document.getElementById('editor_style');
/*
@@ -1902,3 +1903,5 @@ window.addEventListener('load', function () {
toggleVisibleWhitespace();
}
});
/** @} */

View File

@@ -1,12 +1,17 @@
/**
** \defgroup tfcore Tilde Friends Core JS
** Tilde Friends process management, in JavaScript.
** @{
*/
* \file
* \defgroup tfcore Tilde Friends Core JS
* Tilde Friends process management, in JavaScript.
* @{
*/
/** \cond */
import * as app from './app.js';
import * as http from './http.js';
export {invoke, getProcessBlob};
/** \endcond */
/** All running processes. */
let gProcesses = {};
/** Whether stats are currently being sent. */
@@ -17,9 +22,9 @@ let g_handler_index = 0;
const k_ping_interval = 60 * 1000;
/**
** Print an error.
** @param error The error.
*/
* Print an error.
* @param error The error.
*/
function printError(error) {
if (error.stackTrace) {
print(error.fileName + ':' + error.lineNumber + ': ' + error.message);
@@ -33,11 +38,11 @@ function printError(error) {
}
/**
** Invoke a handler.
** @param handlers The handlers on which to invoke the callback.
** @param argv Arguments to pass to the handlers.
** @return A promise.
*/
* 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 = [];
if (handlers) {
@@ -59,11 +64,11 @@ function invoke(handlers, argv) {
}
/**
** 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.
*/
* 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 = [];
for (let process of Object.values(gProcesses)) {
@@ -75,10 +80,10 @@ function broadcastEvent(eventName, argv) {
}
/**
** Send a message to all other instances of the same app.
** @param message The message.
** @return A promise.
*/
* Send a message to all other instances of the same app.
* @param message The message.
* @return A promise.
*/
function broadcast(message) {
let sender = this;
let promises = [];
@@ -96,14 +101,14 @@ function broadcast(message) {
}
/**
** 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.
*/
* 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,
packageOwner,
@@ -127,10 +132,10 @@ function broadcastAppEventToUser(
}
/**
** Get user context information for a call.
** @param caller The calling process.
** @param process The receiving process.
*/
* Get user context information for a call.
* @param caller The calling process.
* @param process The receiving process.
*/
function getUser(caller, process) {
return {
key: process.key,
@@ -142,12 +147,12 @@ function getUser(caller, process) {
}
/**
** Send a message.
** @param from The calling process.
** @param to The receiving process.
** @param message The message.
** @return A promise.
*/
* 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']) {
return invoke(to.eventHandlers['message'], [getUser(from, from), message]);
@@ -155,12 +160,12 @@ function postMessageInternal(from, to, message) {
}
/**
** 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.
*/
* 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) {
let process = gProcesses[key];
if (!process && !(options && 'create' in options && !options.create)) {
@@ -711,9 +716,9 @@ ssb.addEventListener('connections', function () {
});
/**
** Load settings from the database.
** @return The settings as a key value pairs object.
*/
* Load settings from the database.
* @return The settings as a key value pairs object.
*/
async function loadSettings() {
let data = {};
try {
@@ -733,8 +738,8 @@ async function loadSettings() {
}
/**
** Send periodic stats to all clients.
*/
* Send periodic stats to all clients.
*/
function sendStats() {
let apps = Object.values(gProcesses)
.filter((process) => process.app)
@@ -751,15 +756,15 @@ function sendStats() {
}
/**
** 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.
*/
* 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,
@@ -826,5 +831,3 @@ exports.callAppHandler = async function callAppHandler(
};
/** @} */
export { invoke, getProcessBlob };

View File

@@ -1,6 +1,17 @@
/**
* \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 = {};
/**
@@ -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;
}
/** @} */