Skip to content

Commit 0cb0e3f

Browse files
b3008claude
andcommitted
Add aa-signal-protocol element for ESM signalling schedules
Declarative custom element that specifies when and how participants are signalled in signal-contingent ESM/EMA studies. Supports fixed, random, and stratified-random scheduling with time windows, weekend overrides, exclusions, and a getSignallingTimes() method that computes concrete signal times for external scheduling systems. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent fcbbae5 commit 0cb0e3f

47 files changed

Lines changed: 2248 additions & 1032 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/customElements/aa-signal-protocol/aa-signal-protocol.ts

Lines changed: 646 additions & 0 deletions
Large diffs are not rendered by default.

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export { AAMemory } from './customElements/aa-memory/aa-memory';
2626
export { AASequence } from './customElements/aa-sequence/aa-sequence';
2727
export { AAJump } from './customElements/aa-sequence/aa-jump/aa-jump';
2828
export { AASession, type AASessionInput, type AASessionInputSubmitEventDetail } from './customElements/aa-session/aa-session';
29+
export { AASignalProtocol, type SignalProtocolConfig } from './customElements/aa-signal-protocol/aa-signal-protocol';
2930
export { AASlider } from './customElements/aa-slider/aa-slider';
3031
export { AATextAnswer } from './customElements/aa-text-answer/aa-text-answer';
3132
export { AAVariable } from './customElements/aa-variable/aa-variable';
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import type { Meta, StoryObj } from "@storybook/web-components";
2+
import { html } from "lit";
3+
import { unsafeHTML } from "lit/directives/unsafe-html.js";
4+
5+
const classicESM = `<aa-signal-protocol
6+
name="classic-esm"
7+
schedule-type="stratified-random"
8+
signals-per-day="8"
9+
window-start="09:00"
10+
window-end="22:00"
11+
min-interval="20min"
12+
signal-expiry="15min"
13+
reminder-after="5min"
14+
study-days="14">
15+
<aa-session name="daily-experience" should-run="false">
16+
<template><div>Assessment content here</div></template>
17+
</aa-session>
18+
</aa-signal-protocol>`;
19+
20+
const weekdayWeekendSplit = `<aa-signal-protocol
21+
name="work-stress"
22+
schedule-type="stratified-random"
23+
signals-per-day="6"
24+
signal-windows="08:00-12:00;13:00-20:00"
25+
min-interval="30min"
26+
signal-expiry="20min"
27+
weekend-signals-per-day="4"
28+
weekend-signal-windows="10:00-21:00"
29+
start-date="2025-09-01"
30+
end-date="2025-09-21">
31+
<aa-session name="work-stress-assessment" should-run="false">
32+
<template><div>Assessment content here</div></template>
33+
</aa-session>
34+
</aa-signal-protocol>`;
35+
36+
const fixedDiaryPlusRandom = `<aa-signal-protocol
37+
name="mixed-protocol"
38+
schedule-type="stratified-random"
39+
signals-per-day="5"
40+
window-start="10:00"
41+
window-end="20:00"
42+
min-interval="30min"
43+
fixed-times="07:30,22:00"
44+
fixed-times-label="diary"
45+
study-days="7">
46+
<aa-session name="mood-diary" should-run="false">
47+
<template><div>Assessment content here</div></template>
48+
</aa-session>
49+
</aa-signal-protocol>`;
50+
51+
const fixedTimeOnly = `<aa-signal-protocol
52+
name="daily-diary"
53+
schedule-type="fixed"
54+
fixed-times="08:00,14:00,21:00"
55+
signal-expiry="30min"
56+
reminder-after="15min"
57+
reminder-count="2"
58+
active-days="weekdays"
59+
study-days="21">
60+
<aa-session name="diary-assessment" should-run="false">
61+
<template><div>Assessment content here</div></template>
62+
</aa-session>
63+
</aa-signal-protocol>`;
64+
65+
function renderProtocolSummary(sourceCode: string) {
66+
return html`
67+
<div style="font-family: monospace; padding: 16px;">
68+
<pre style="background: #f5f5f5; padding: 16px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap;">${sourceCode}</pre>
69+
${unsafeHTML(sourceCode)}
70+
<script type="module">
71+
setTimeout(() => {
72+
const protocol = document.querySelector('aa-signal-protocol');
73+
if (protocol && protocol.getProtocol) {
74+
const config = protocol.getProtocol();
75+
const summary = document.getElementById('protocol-summary');
76+
if (summary) {
77+
summary.textContent = JSON.stringify(config, null, 2);
78+
}
79+
}
80+
}, 100);
81+
</script>
82+
<h3 style="font-family: sans-serif; margin-top: 16px;">Parsed Protocol</h3>
83+
<pre id="protocol-summary" style="background: #e8f5e9; padding: 16px; border-radius: 4px; overflow-x: auto; white-space: pre-wrap;"></pre>
84+
</div>
85+
`;
86+
}
87+
88+
const meta = {
89+
title: "Data & Utility/SignalProtocol",
90+
tags: ["autodocs"],
91+
} satisfies Meta;
92+
93+
export default meta;
94+
type Story = StoryObj;
95+
96+
export const ClassicStratifiedRandomESM: Story = {
97+
render: () => renderProtocolSummary(classicESM),
98+
parameters: {
99+
docs: {
100+
source: { code: classicESM, language: "html" },
101+
},
102+
},
103+
};
104+
105+
export const WeekdayWeekendSplit: Story = {
106+
render: () => renderProtocolSummary(weekdayWeekendSplit),
107+
parameters: {
108+
docs: {
109+
source: { code: weekdayWeekendSplit, language: "html" },
110+
},
111+
},
112+
};
113+
114+
export const FixedDiaryPlusRandomSignals: Story = {
115+
render: () => renderProtocolSummary(fixedDiaryPlusRandom),
116+
parameters: {
117+
docs: {
118+
source: { code: fixedDiaryPlusRandom, language: "html" },
119+
},
120+
},
121+
};
122+
123+
export const FixedTimeOnly: Story = {
124+
render: () => renderProtocolSummary(fixedTimeOnly),
125+
parameters: {
126+
docs: {
127+
source: { code: fixedTimeOnly, language: "html" },
128+
},
129+
},
130+
};

storybook-static/assets/Choose.stories-BDFeRZB1.js

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import{b as t}from"./lit-element-BrtkDOYA.js";import{o as l}from"./unsafe-html-DX_ZMqK7.js";import"./directive-CF8sV3Lr.js";const e=`<aa-session name="choose-demo" debug>
2+
<aa-variable name="score" value="75"></aa-variable>
3+
<aa-choose name="grade" debug>
4+
<aa-when test="score >= 90">
5+
<aa-label>Grade: A — Excellent!</aa-label>
6+
</aa-when>
7+
<aa-when test="score >= 70">
8+
<aa-label>Grade: B — Good job!</aa-label>
9+
</aa-when>
10+
<aa-otherwise>
11+
<aa-label>Grade: C — Keep trying!</aa-label>
12+
</aa-otherwise>
13+
</aa-choose>
14+
</aa-session>`,m={title:"Control Flow/Choose",tags:["autodocs"],render:()=>t`${l(e)}`,parameters:{docs:{source:{code:e,language:"html"}}}},a={};var o,r,s;a.parameters={...a.parameters,docs:{...(o=a.parameters)==null?void 0:o.docs,source:{originalSource:"{}",...(s=(r=a.parameters)==null?void 0:r.docs)==null?void 0:s.source}}};const i=["Default"];export{a as Default,i as __namedExportsOrder,m as default};

storybook-static/assets/Color-6VNJS4EI-37-XCFj0.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

storybook-static/assets/Color-6VNJS4EI-DCVp-N4U.js

Lines changed: 0 additions & 1 deletion
This file was deleted.

storybook-static/assets/DocsRenderer-NNNQARDV-Jj6t0ik3.js

Lines changed: 7 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import{b as x}from"./lit-element-BrtkDOYA.js";import{o as f}from"./unsafe-html-DX_ZMqK7.js";import"./directive-CF8sV3Lr.js";const b=(e,r,g)=>`<aa-session name="${e}">
2+
<div style="display:flex; align-items:center; gap:12px;">
3+
<button onclick="
4+
var container = this.parentElement.querySelector('.result-container');
5+
container.innerHTML = '';
6+
var el = document.createElement('aa-function-random');
7+
el.setAttribute('name', 'randomValue');
8+
el.setAttribute('min', '${r}');
9+
el.setAttribute('max', '${g}');
10+
el.setAttribute('debug', '');
11+
container.appendChild(el);
12+
this.parentElement.querySelector('.result-value').textContent = el.value;
13+
">Generate</button>
14+
<span>Result: <strong class="result-value">—</strong></span>
15+
<span class="result-container" style="display:none;"></span>
16+
</div>
17+
</aa-session>`,D={title:"Data & Utility/Function Random",tags:["autodocs"],render:e=>{const r=b("random-demo",e.min,e.max);return x`${f(r)}`},parameters:{docs:{source:{code:'<aa-function-random name="randomValue" min="1" max="100"></aa-function-random>',language:"html"}}}},a={args:{min:1,max:100}},n={args:{min:1,max:6}},t={args:{min:0,max:1}};var s,o,m;a.parameters={...a.parameters,docs:{...(s=a.parameters)==null?void 0:s.docs,source:{originalSource:`{
18+
args: {
19+
min: 1,
20+
max: 100
21+
}
22+
}`,...(m=(o=a.parameters)==null?void 0:o.docs)==null?void 0:m.source}}};var i,c,l;n.parameters={...n.parameters,docs:{...(i=n.parameters)==null?void 0:i.docs,source:{originalSource:`{
23+
args: {
24+
min: 1,
25+
max: 6
26+
}
27+
}`,...(l=(c=n.parameters)==null?void 0:c.docs)==null?void 0:l.source}}};var u,d,p;t.parameters={...t.parameters,docs:{...(u=t.parameters)==null?void 0:u.docs,source:{originalSource:`{
28+
args: {
29+
min: 0,
30+
max: 1
31+
}
32+
}`,...(p=(d=t.parameters)==null?void 0:d.docs)==null?void 0:p.source}}};const h=["Default","DiceRoll","CoinFlip"];export{t as CoinFlip,a as Default,n as DiceRoll,h as __namedExportsOrder,D as default};

storybook-static/assets/FunctionRandom.stories-CAw6z2ed.js

Lines changed: 0 additions & 23 deletions
This file was deleted.

0 commit comments

Comments
 (0)