Shared messages: working together with the gallery tools
So that many tools fit together without each one knowing every other, the gallery tools speak a shared language. Your own tools can speak it too.
Instead of wiring every tool to every other, there are six message kinds. Each tool speaks only the kinds that suit it. A few tools collect what others report: these are the collection points. Others can be opened with settings already in place. That way every tool can work with many others without knowing more than two or three names.
Technically nothing is new: everything runs through ARTEFAKT.senden, eingang and oeffnen, as described on the page “Linking tools”. The only new thing is the agreement on what a message looks like.
The five collection points
| Collection point | Question it answers | understands |
|---|---|---|
| Habit chain | What have I done? | erledigt |
| Reward chart | What has the child achieved? | erledigt |
| Shopping list | What do I need? | einkauf |
| Budget book | What does it cost? | betrag |
| Evening review | What happened today? | notiz |
In German they are called Gewohnheits-Kette, Belohnungstafel, Einkaufsliste, Haushaltsbuch and Tagesabschluss. The name is the address: the English version of a tool sends to the English names.
The shared envelope
Every message has the same four fields. The text field makes it understandable even for tools that do not know the kind: they can simply show the text. Who sent it and when is added by the toolbox itself (von and zeit in the inbox).
{
typ: 'erledigt', // one of the six kinds
v: 1, // version of the kind
id: 'lq2x9k-4f7a', // unique, against duplicates (at most 64 characters)
text: 'Breathing, 5 minutes', // always readable (at most 500 characters)
/* … fields of the kind … */
}The field names and kinds are the same in every language (they come from the toolbox’s German interface). Only contents such as text, was or artikel are in the language of the tool.
The six kinds
| Kind | Meaning | Fields (besides the envelope) |
|---|---|---|
| einkauf | “I need this” or “I bought this” (shopping) | artikel: list of texts (required, at most 120 characters each); aktion: 'brauche' (need) or 'gekauft' (bought); anlass (occasion); posten: [{ name, menge, einheit }] for tools that calculate |
| erledigt | “I did this” (done) | was (what, required, at most 80 characters); menge (amount); einheit (unit, e.g. 'min'); datum as YYYY-MM-DD; wer (who, for the reward chart) |
| betrag | “This is what it costs” (amount) | betrag as a number (required); waehrung (currency, e.g. 'EUR'); art: 'ausgabe' (expense), 'fix' (monthly) or 'plan'; topf (pot); datum |
| notiz | “This happened” (note) | text (required); datum |
| personen | “These people are in” (people) | gruppen: [{ name, namen: [ … ] }] |
| start | “Get ready like this” | einstellung: an excerpt from the receiver’s CUSTOMIZE block; then ARTEFAKT.oeffnen(target) |
{ typ: 'einkauf', v: 1, id, text: 'Lasagne: 500 g minced meat, 2 tins of tomatoes',
aktion: 'brauche', anlass: 'Lasagne',
artikel: ['500 g minced meat', '2 tins of tomatoes'],
posten: [{ name: 'Minced meat', menge: 500, einheit: 'g' }, { name: 'Tomatoes', menge: 2, einheit: 'tin' }] }
{ typ: 'erledigt', v: 1, id, text: 'Practice, 20 minutes', was: 'Practice', menge: 20, einheit: 'min', datum: '2026-10-04', wer: 'Mia' }
{ typ: 'betrag', v: 1, id, text: 'Groceries, €23.40', betrag: 23.40, waehrung: 'EUR', art: 'ausgabe', topf: 'Food', datum: '2026-10-04' }
{ typ: 'notiz', v: 1, id, text: 'Decision: flat B (4.2 to 3.8 points)', datum: '2026-10-04' }
{ typ: 'personen', v: 1, id, text: 'Here today: Mia, Tom, Ali', gruppen: [{ name: 'Today', namen: ['Mia', 'Tom', 'Ali'] }] }
{ typ: 'start', v: 1, id, text: 'Metronome: 92 BPM, 3/4', einstellung: { tempo: 92, takt: '3/4' } }
// then: ARTEFAKT.oeffnen('Metronome')Remote control with “start”
With “start” one tool sets up another for one run, for example the songbook opens the metronome at 72 beats per minute. The receiver takes over only the settings it has released in KONFIG.vonAussen, only with the matching data type and only for this run. Your stored settings stay unchanged, and a banner shows who set what, with a button to reset it.
Ten rules
Every tool runs on its own
Linking is an extra. Without window.ARTEFAKT only the send buttons disappear.
Send only after an action
A button or the end of an exercise triggers sending. Nothing is sent secretly.
Receiving never triggers sending
That way no loop can arise between two tools.
Pick up on start and on return
Call ARTEFAKT.eingang() on start and on visibilitychange so nothing is left behind.
Check strictly
Only known typ, only v up to your own version, check fields for type and length, ignore everything else. Display only with textContent.
No duplicates
The receiver remembers the last 200 ids.
Targets are adjustable
Target names live in the CUSTOMIZE block, for example KONFIG.ziele = { erledigt: 'Habit chain' }.
Origin visible, takeover reversible
Every taken-over entry shows “from …” and can be undone.
Bundle
One action is one message, for example all ingredients together. The inbox holds at most 100 entries.
Islands are fine
Not every tool has to be linked. An emergency card, for example, sends and receives nothing.
Receiving: how to check strictly
const UNDERSTANDS = 'erledigt';
const text = (v, max) => typeof v === 'string' && v.length <= max;
const num = v => v == null || (typeof v === 'number' && Number.isFinite(v));
const day = v => v == null || (typeof v === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(v));
function valid(n) {
if (!n || typeof n !== 'object' || n.typ !== UNDERSTANDS) return false; // only our kind
if (n.v != null && n.v > 1) return false; // newer version: better not
if (n.text != null && !text(n.text, 500)) return false;
return text(n.was, 80) && n.was.trim() !== '' && num(n.menge) && day(n.datum);
}
function pickUp() {
if (!window.ARTEFAKT || typeof ARTEFAKT.eingang !== 'function') return;
let list = [];
try { list = ARTEFAKT.eingang(); } catch { return; }
const seen = read('ids', []);
for (const e of list) {
const n = e && e.daten;
if (!valid(n)) continue; // ignore the unknown
if (n.id) { if (seen.includes(n.id)) continue; seen.push(n.id); }
addEntry({ what: n.was, amount: n.menge, date: n.datum, from: String(e.von || '?') });
}
write('ids', seen.slice(-200));
render(); // display with textContent
}
pickUp();
document.addEventListener('visibilitychange', () => { if (document.visibilityState === 'visible') pickUp(); });Prompt: your own tool that joins in
Create a tool for me as ONE single HTML file for the Artefakt Toolbox: [goal in one sentence].
Working together using the shared message format of the Artefakt gallery (field names and kinds stay exactly as written):
- Envelope of every message: { typ, v: 1, id (unique, at most 64 characters), text (readable, at most 500 characters), … }.
- The tool SENDS, after a tap on [button], a message of the kind [e.g. 'erledigt' with was, menge, einheit, datum as YYYY-MM-DD] with ARTEFAKT.senden(target, message). The target name is adjustable in the CUSTOMIZE block: KONFIG.ziele = { [kind]: '[e.g. Habit chain]' }; empty = off.
- The tool UNDERSTANDS [kind or “nothing”]: it picks up on start and on visibilitychange with ARTEFAKT.eingang(), accepts only this kind with v at most 1, checks all fields for type and length, ignores everything else, remembers the last 200 ids against duplicates, shows the origin (“from …”) and offers “Undo”.
- Receiving never triggers sending. All interaction runs through ONE object in the code (check, send, pick up, open) so that it can easily be replaced later.
- Before each use, check that window.ARTEFAKT and the function exist. Without the toolbox the tool runs fully, just without send buttons.
- Show foreign texts only with textContent, never with innerHTML.
Technical: one single HTML file, no external files, localStorage with try/catch, works offline, mobile first, dark design, tap targets at least 48 px, English. Output the complete file in one block.See also: Linking tools · Gallery blueprint · Outlook: connection layer