fd0a7868f3
- Task 3: Extended FoundryAdapter with user flag access methods
- Added getFlag(userId, scope, key) method
- Added setFlag(userId, scope, key, value) method
- Added getFlagModule(userId, key) convenience method
- Added setFlagModule(userId, key, value) convenience method
- Task 4: Integrated Privacy Settings with Director's Board
- Updated participant-card.hbs to show Reaction Cam badge
- Modified boardUtils.js to pass playerPrivacyManager through context
- Updated DirectorsBoard to accept and pass playerPrivacyManager
- Added CSS styles for Reaction Cam badge (SP accent color)
- Task 5: Registered PlayerPrivacyPanel in module settings
- Added settings menu registration in module.js Hooks.once('ready')
- Available to all users (restricted: false)
- Uses localized labels and hints
- Task 6: Added all localization strings
- Added SCRYING_POOL.PrivacyPanel.* strings for panel UI
- Added SCRYING_POOL.Settings.* strings for settings menu
- Updated story file with task completion status
Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
136 lines
4.7 KiB
Plaintext
136 lines
4.7 KiB
Plaintext
/**
|
||
* styles/components/_participant-card.less
|
||
*
|
||
* 80×100px card tile used in the Director's Board grid.
|
||
* All selectors scoped under .scrying-pool.
|
||
* Uses --sp-* tokens only — no Foundry --color-* / --font-* / --border-* tokens.
|
||
*/
|
||
|
||
.scrying-pool .participant-card {
|
||
width: 80px;
|
||
height: 100px;
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
border: 2px solid var(--sp-border);
|
||
border-radius: 4px;
|
||
background: var(--sp-surface);
|
||
cursor: pointer;
|
||
overflow: hidden;
|
||
transition:
|
||
border-color var(--sp-transition-state),
|
||
background-color var(--sp-transition-state);
|
||
|
||
// ── State ring colour + shape signals (second-signal rule) ──────────────
|
||
&.sp-state-active { border-color: var(--sp-state-active-border); border-style: solid; background: var(--sp-state-active-bg); }
|
||
&.sp-state-hidden { border-color: var(--sp-state-hidden-border); border-style: dashed; background: var(--sp-state-hidden-bg); }
|
||
&.sp-state-self-muted { border-color: var(--sp-state-self-muted-border); border-style: solid; background: var(--sp-state-self-muted-bg); }
|
||
&.sp-state-offline { border-color: var(--sp-state-offline-border); border-style: none; background: var(--sp-state-offline-bg); }
|
||
&.sp-state-cam-lost { border-color: var(--sp-state-cam-lost-border); border-style: dashed; background: var(--sp-state-cam-lost-bg); }
|
||
&.sp-state-reconnecting { border-color: var(--sp-state-reconnecting-border); border-style: solid; background: var(--sp-state-reconnecting-bg); }
|
||
&.sp-state-never-connected { border-color: var(--sp-state-never-connected-border); border-style: none; background: var(--sp-state-never-connected-bg); }
|
||
&.sp-state-ghost { border-color: var(--sp-state-ghost-border); border-style: dotted; background: var(--sp-state-ghost-bg); }
|
||
&.sp-state-pending { border-color: var(--sp-state-pending-border); border-style: solid; background: var(--sp-state-pending-bg); }
|
||
|
||
// ── Avatar ──────────────────────────────────────────────────────────────
|
||
&__avatar {
|
||
width: 48px;
|
||
height: 48px;
|
||
margin: 8px auto 4px;
|
||
flex-shrink: 0;
|
||
position: relative;
|
||
|
||
img {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
object-fit: cover;
|
||
display: block;
|
||
}
|
||
|
||
// ── Badge (Reaction Cam enabled indicator) ─────────────────────────────
|
||
&-badge {
|
||
position: absolute;
|
||
width: 16px;
|
||
height: 16px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 8px;
|
||
border: 1px solid var(--sp-surface);
|
||
box-shadow: 0 0 0 2px var(--sp-surface);
|
||
|
||
i {
|
||
font-size: 10px;
|
||
color: var(--sp-text-primary);
|
||
}
|
||
|
||
&--reaction-cam {
|
||
background: var(--sp-accent);
|
||
bottom: 0;
|
||
right: 0;
|
||
cursor: help;
|
||
}
|
||
}
|
||
}
|
||
|
||
// ── Name (12px, 2-line truncate) ─────────────────────────────────────────
|
||
&__name {
|
||
font-size: 12px;
|
||
line-height: 1.2;
|
||
text-align: center;
|
||
color: var(--sp-text-primary);
|
||
overflow: hidden;
|
||
display: -webkit-box;
|
||
-webkit-line-clamp: 2;
|
||
-webkit-box-orient: vertical;
|
||
padding: 0 4px;
|
||
margin: 0;
|
||
width: 100%;
|
||
word-break: break-word;
|
||
}
|
||
|
||
// ── Toggle overlay (shown on hover / focus-within) ───────────────────────
|
||
&__toggle {
|
||
position: absolute;
|
||
inset: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: var(--sp-badge-bg);
|
||
color: var(--sp-badge-text);
|
||
border: none;
|
||
border-radius: 2px;
|
||
cursor: pointer;
|
||
opacity: 0;
|
||
transition: opacity var(--sp-transition-state);
|
||
font-size: 18px;
|
||
padding: 0;
|
||
|
||
&:focus-visible {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
&:hover &__toggle,
|
||
&:focus-within &__toggle {
|
||
opacity: 1;
|
||
}
|
||
|
||
// ── Focus ring (inherits module-wide pattern from tokens/_focus.less) ────
|
||
&:focus-visible {
|
||
outline: none;
|
||
box-shadow: var(--sp-focus-ring), 0 0 0 4px var(--sp-surface);
|
||
}
|
||
}
|
||
|
||
// ── Reduced motion ────────────────────────────────────────────────────────
|
||
@media (prefers-reduced-motion: reduce) {
|
||
.scrying-pool .participant-card {
|
||
transition: none;
|
||
&__toggle { transition: none; }
|
||
}
|
||
}
|