atagen

malice

public
Star0
Watch0
05a48e9e79429877d5ccbef7c37361311e3d29db
2
ssh://brewery@pls.lobotomise.me:33/atagen/malice.git

Blame: web/src/lib/SettingsPanel.svelte

df3c0f2b3da4atagen2026-10-22 13:07 UTC1
<script lang="ts">
df3c0f2b3da4atagen2026-10-22 13:07 UTC2
  import { createEventDispatcher } from 'svelte';
df3c0f2b3da4atagen2026-10-22 13:07 UTC3
  import {
df3c0f2b3da4atagen2026-10-22 13:07 UTC4
    LINE_HEIGHT_DEFAULT,
df3c0f2b3da4atagen2026-10-22 13:07 UTC5
    LINE_HEIGHT_MAX,
df3c0f2b3da4atagen2026-10-22 13:07 UTC6
    LINE_HEIGHT_MIN,
df3c0f2b3da4atagen2026-10-22 13:07 UTC7
    MESSAGE_SPACING_DEFAULT,
df3c0f2b3da4atagen2026-10-22 13:07 UTC8
    MESSAGE_SPACING_MAX,
df3c0f2b3da4atagen2026-10-22 13:07 UTC9
    MESSAGE_SPACING_MIN,
df3c0f2b3da4atagen2026-10-22 13:07 UTC10
    THEMES,
df3c0f2b3da4atagen2026-10-22 13:07 UTC11
    accent,
df3c0f2b3da4atagen2026-10-22 13:07 UTC12
    lineHeight,
df3c0f2b3da4atagen2026-10-22 13:07 UTC13
    messageSpacing,
df3c0f2b3da4atagen2026-10-22 13:07 UTC14
    theme
df3c0f2b3da4atagen2026-10-22 13:07 UTC15
  } from './appearance';
df3c0f2b3da4atagen2026-10-22 13:07 UTC16
  import { DEFAULT_QUICK_REACTIONS } from './quick-reactions';
df3c0f2b3da4atagen2026-10-22 13:07 UTC17
  import ReactionPicker from './ReactionPicker.svelte';
df3c0f2b3da4atagen2026-10-22 13:07 UTC18
  import type { ImagePack, QuickReaction } from './types';
df3c0f2b3da4atagen2026-10-22 13:07 UTC19
df3c0f2b3da4atagen2026-10-22 13:07 UTC20
  export let quick: QuickReaction[];
df3c0f2b3da4atagen2026-10-22 13:07 UTC21
  export let packs: ImagePack[] = [];
df3c0f2b3da4atagen2026-10-22 13:07 UTC22
  export let onQuickChange: (list: QuickReaction[]) => void;
df3c0f2b3da4atagen2026-10-22 13:07 UTC23
d095131eec0eatagen2026-10-24 08:39 UTC24
  const dispatch = createEventDispatcher<{ close: void; packs: void }>();
df3c0f2b3da4atagen2026-10-22 13:07 UTC25
df3c0f2b3da4atagen2026-10-22 13:07 UTC26
  $: quickIsDefault =
df3c0f2b3da4atagen2026-10-22 13:07 UTC27
    quick.length === DEFAULT_QUICK_REACTIONS.length && quick.every((r, i) => r.key === DEFAULT_QUICK_REACTIONS[i].key);
df3c0f2b3da4atagen2026-10-22 13:07 UTC28
df3c0f2b3da4atagen2026-10-22 13:07 UTC29
  function themeAccent(): string {
df3c0f2b3da4atagen2026-10-22 13:07 UTC30
    return getComputedStyle(document.documentElement).getPropertyValue('--accent').trim() || '#5865f2';
df3c0f2b3da4atagen2026-10-22 13:07 UTC31
  }
df3c0f2b3da4atagen2026-10-22 13:07 UTC32
</script>
df3c0f2b3da4atagen2026-10-22 13:07 UTC33
df3c0f2b3da4atagen2026-10-22 13:07 UTC34
<div class="modal settings" role="dialog" aria-modal="true" aria-label="Settings">
df3c0f2b3da4atagen2026-10-22 13:07 UTC35
  <div class="modal-tabs" role="tablist" aria-label="Settings">
df3c0f2b3da4atagen2026-10-22 13:07 UTC36
    <button type="button" role="tab" aria-selected="true" class="active">Settings</button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC37
    <button type="button" class="icon-btn modal-x" aria-label="Close settings" on:click={() => dispatch('close')}>
df3c0f2b3da4atagen2026-10-22 13:07 UTC38
      <span aria-hidden="true">✕</span>
df3c0f2b3da4atagen2026-10-22 13:07 UTC39
    </button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC40
  </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC41
df3c0f2b3da4atagen2026-10-22 13:07 UTC42
  <section aria-label="Theme">
df3c0f2b3da4atagen2026-10-22 13:07 UTC43
    <h3 class="sec-h">Theme</h3>
df3c0f2b3da4atagen2026-10-22 13:07 UTC44
    <div class="themes" role="radiogroup" aria-label="Theme">
df3c0f2b3da4atagen2026-10-22 13:07 UTC45
      {#each THEMES as t (t.id)}
df3c0f2b3da4atagen2026-10-22 13:07 UTC46
        <button
df3c0f2b3da4atagen2026-10-22 13:07 UTC47
          type="button"
df3c0f2b3da4atagen2026-10-22 13:07 UTC48
          role="radio"
df3c0f2b3da4atagen2026-10-22 13:07 UTC49
          aria-checked={$theme === t.id}
df3c0f2b3da4atagen2026-10-22 13:07 UTC50
          class="theme-opt"
df3c0f2b3da4atagen2026-10-22 13:07 UTC51
          class:active={$theme === t.id}
df3c0f2b3da4atagen2026-10-22 13:07 UTC52
          data-swatch={t.id}
df3c0f2b3da4atagen2026-10-22 13:07 UTC53
          on:click={() => theme.set(t.id)}
df3c0f2b3da4atagen2026-10-22 13:07 UTC54
        >
df3c0f2b3da4atagen2026-10-22 13:07 UTC55
          <span class="swatch" aria-hidden="true"></span>
df3c0f2b3da4atagen2026-10-22 13:07 UTC56
          {t.label}
df3c0f2b3da4atagen2026-10-22 13:07 UTC57
        </button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC58
      {/each}
df3c0f2b3da4atagen2026-10-22 13:07 UTC59
    </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC60
    <div class="range accent-row">
df3c0f2b3da4atagen2026-10-22 13:07 UTC61
      <label for="accent-colour">Accent colour</label>
df3c0f2b3da4atagen2026-10-22 13:07 UTC62
      <input id="accent-colour" type="color" value={$accent ?? themeAccent()} on:input={(e) => accent.set(e.currentTarget.value)} />
df3c0f2b3da4atagen2026-10-22 13:07 UTC63
      <button type="button" class="ghost-btn" disabled={$accent === null} on:click={() => accent.set(null)}>Use theme colour</button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC64
    </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC65
  </section>
df3c0f2b3da4atagen2026-10-22 13:07 UTC66
df3c0f2b3da4atagen2026-10-22 13:07 UTC67
  <section aria-label="Message spacing">
df3c0f2b3da4atagen2026-10-22 13:07 UTC68
    <h3 class="sec-h">Space between messages</h3>
df3c0f2b3da4atagen2026-10-22 13:07 UTC69
    <div class="range">
df3c0f2b3da4atagen2026-10-22 13:07 UTC70
      <input
df3c0f2b3da4atagen2026-10-22 13:07 UTC71
        type="range"
df3c0f2b3da4atagen2026-10-22 13:07 UTC72
        min={MESSAGE_SPACING_MIN}
df3c0f2b3da4atagen2026-10-22 13:07 UTC73
        max={MESSAGE_SPACING_MAX}
df3c0f2b3da4atagen2026-10-22 13:07 UTC74
        step="1"
df3c0f2b3da4atagen2026-10-22 13:07 UTC75
        bind:value={$messageSpacing}
df3c0f2b3da4atagen2026-10-22 13:07 UTC76
        aria-label="Space between messages in pixels"
df3c0f2b3da4atagen2026-10-22 13:07 UTC77
      />
df3c0f2b3da4atagen2026-10-22 13:07 UTC78
      <span class="value">{$messageSpacing}px</span>
df3c0f2b3da4atagen2026-10-22 13:07 UTC79
      <button
df3c0f2b3da4atagen2026-10-22 13:07 UTC80
        type="button"
df3c0f2b3da4atagen2026-10-22 13:07 UTC81
        class="ghost-btn"
df3c0f2b3da4atagen2026-10-22 13:07 UTC82
        disabled={$messageSpacing === MESSAGE_SPACING_DEFAULT}
df3c0f2b3da4atagen2026-10-22 13:07 UTC83
        on:click={() => messageSpacing.set(MESSAGE_SPACING_DEFAULT)}
df3c0f2b3da4atagen2026-10-22 13:07 UTC84
      >
df3c0f2b3da4atagen2026-10-22 13:07 UTC85
        Reset
df3c0f2b3da4atagen2026-10-22 13:07 UTC86
      </button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC87
    </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC88
    <h3 class="sec-h">Line height</h3>
df3c0f2b3da4atagen2026-10-22 13:07 UTC89
    <div class="range">
df3c0f2b3da4atagen2026-10-22 13:07 UTC90
      <input
df3c0f2b3da4atagen2026-10-22 13:07 UTC91
        type="range"
df3c0f2b3da4atagen2026-10-22 13:07 UTC92
        min={LINE_HEIGHT_MIN}
df3c0f2b3da4atagen2026-10-22 13:07 UTC93
        max={LINE_HEIGHT_MAX}
df3c0f2b3da4atagen2026-10-22 13:07 UTC94
        step="0.05"
df3c0f2b3da4atagen2026-10-22 13:07 UTC95
        bind:value={$lineHeight}
df3c0f2b3da4atagen2026-10-22 13:07 UTC96
        aria-label="Line height within messages"
df3c0f2b3da4atagen2026-10-22 13:07 UTC97
      />
df3c0f2b3da4atagen2026-10-22 13:07 UTC98
      <span class="value">{$lineHeight.toFixed(2)}</span>
df3c0f2b3da4atagen2026-10-22 13:07 UTC99
      <button
df3c0f2b3da4atagen2026-10-22 13:07 UTC100
        type="button"
df3c0f2b3da4atagen2026-10-22 13:07 UTC101
        class="ghost-btn"
df3c0f2b3da4atagen2026-10-22 13:07 UTC102
        disabled={$lineHeight === LINE_HEIGHT_DEFAULT}
df3c0f2b3da4atagen2026-10-22 13:07 UTC103
        on:click={() => lineHeight.set(LINE_HEIGHT_DEFAULT)}
df3c0f2b3da4atagen2026-10-22 13:07 UTC104
      >
df3c0f2b3da4atagen2026-10-22 13:07 UTC105
        Reset
df3c0f2b3da4atagen2026-10-22 13:07 UTC106
      </button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC107
    </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC108
  </section>
df3c0f2b3da4atagen2026-10-22 13:07 UTC109
df3c0f2b3da4atagen2026-10-22 13:07 UTC110
  <section aria-label="Quick reactions">
df3c0f2b3da4atagen2026-10-22 13:07 UTC111
    <div class="sec-head">
df3c0f2b3da4atagen2026-10-22 13:07 UTC112
      <h3 class="sec-h">Quick reactions</h3>
df3c0f2b3da4atagen2026-10-22 13:07 UTC113
      <button type="button" class="ghost-btn" disabled={quickIsDefault} on:click={() => onQuickChange(DEFAULT_QUICK_REACTIONS)}>
df3c0f2b3da4atagen2026-10-22 13:07 UTC114
        Reset
df3c0f2b3da4atagen2026-10-22 13:07 UTC115
      </button>
df3c0f2b3da4atagen2026-10-22 13:07 UTC116
    </div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC117
    <ReactionPicker embedded {quick} {packs} {onQuickChange} onPick={() => {}} onClose={() => dispatch('close')} />
df3c0f2b3da4atagen2026-10-22 13:07 UTC118
  </section>
d095131eec0eatagen2026-10-24 08:39 UTC119
d095131eec0eatagen2026-10-24 08:39 UTC120
  <section aria-label="Packs">
d095131eec0eatagen2026-10-24 08:39 UTC121
    <div class="sec-head">
d095131eec0eatagen2026-10-24 08:39 UTC122
      <h3 class="sec-h">Sticker and emoji packs</h3>
d095131eec0eatagen2026-10-24 08:39 UTC123
      <button type="button" class="ghost-btn" on:click={() => dispatch('packs')}>Manage</button>
d095131eec0eatagen2026-10-24 08:39 UTC124
    </div>
d095131eec0eatagen2026-10-24 08:39 UTC125
  </section>
df3c0f2b3da4atagen2026-10-22 13:07 UTC126
</div>
df3c0f2b3da4atagen2026-10-22 13:07 UTC127
df3c0f2b3da4atagen2026-10-22 13:07 UTC128
<style>
df3c0f2b3da4atagen2026-10-22 13:07 UTC129
  .settings { max-height: calc(100dvh - 32px); overflow-y: auto; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC130
  section + section { margin-top: 18px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC131
  .sec-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 6px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC132
  .sec-head .sec-h { margin: 0; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC133
  .sec-h { font-size: 12px; text-transform: uppercase; letter-spacing: 0.08em; color: var(--muted); margin: 0 0 6px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC134
  .range + .sec-h { margin-top: 12px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC135
  .range { display: flex; align-items: center; gap: 10px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC136
  .range input[type='range'] { flex: 1; accent-color: var(--accent); }
df3c0f2b3da4atagen2026-10-22 13:07 UTC137
  .value { font-size: 13px; min-width: 4ch; text-align: right; font-variant-numeric: tabular-nums; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC138
  .themes { display: grid; grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); gap: 6px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC139
  .theme-opt {
df3c0f2b3da4atagen2026-10-22 13:07 UTC140
    display: flex; align-items: center; gap: 8px;
df3c0f2b3da4atagen2026-10-22 13:07 UTC141
    padding: 6px 8px;
df3c0f2b3da4atagen2026-10-22 13:07 UTC142
    background: var(--bg-soft);
df3c0f2b3da4atagen2026-10-22 13:07 UTC143
    border: 1px solid var(--border);
05a48e9e7942atagen2026-10-24 09:32 UTC144
    border-radius: var(--r-md);
df3c0f2b3da4atagen2026-10-22 13:07 UTC145
    color: var(--text);
df3c0f2b3da4atagen2026-10-22 13:07 UTC146
    font-size: 13px;
df3c0f2b3da4atagen2026-10-22 13:07 UTC147
    cursor: pointer;
df3c0f2b3da4atagen2026-10-22 13:07 UTC148
    text-align: left;
df3c0f2b3da4atagen2026-10-22 13:07 UTC149
  }
df3c0f2b3da4atagen2026-10-22 13:07 UTC150
  .theme-opt.active { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
df3c0f2b3da4atagen2026-10-22 13:07 UTC151
  .swatch { flex: none; width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--border); }
df3c0f2b3da4atagen2026-10-22 13:07 UTC152
  [data-swatch='system'] .swatch { background: linear-gradient(135deg, #14151c 50%, #f7f7fa 50%); }
df3c0f2b3da4atagen2026-10-22 13:07 UTC153
  [data-swatch='dark'] .swatch { background: #14151c; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC154
  [data-swatch='light'] .swatch { background: #f7f7fa; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC155
  [data-swatch='black'] .swatch { background: #000; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC156
  [data-swatch='nord'] .swatch { background: #2e3440; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC157
  .accent-row { margin-top: 10px; font-size: 13px; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC158
  .accent-row label { flex: 1; }
05a48e9e7942atagen2026-10-24 09:32 UTC159
  .accent-row input[type='color'] { width: 36px; height: 26px; padding: 0; border: 1px solid var(--border); border-radius: var(--r-sm); background: none; cursor: pointer; }
df3c0f2b3da4atagen2026-10-22 13:07 UTC160
</style>