-
-
Notifications
You must be signed in to change notification settings - Fork 202
Expand file tree
/
Copy pathcombine.html
More file actions
145 lines (144 loc) · 9.69 KB
/
Copy pathcombine.html
File metadata and controls
145 lines (144 loc) · 9.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="referrer" content="no-referrer">
<title>Combine overlays — Social Stream Ninja</title>
<meta name="description" content="Layer chat and alerts in one browser tab or browser source. Paste your overlay links and copy one combined link.">
<link rel="icon" href="icons/favicon.ico">
<style>
:root { color-scheme: light; --page: #f3f5f9; --card: #fff; --text: #15181f; --muted: #566173; --border: #cdd4e0; --accent: #6930cc; --error: #b42318; }
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; font-family: system-ui, "Segoe UI", sans-serif; background: transparent; color: var(--text); }
[hidden] { display: none !important; }
body.editing { background: var(--page); }
a { color: var(--accent); }
button, input, textarea { font: inherit; }
button, .button { min-height: 44px; padding: 10px 16px; border: 1px solid var(--border); border-radius: 8px; background: var(--card); color: var(--text); cursor: pointer; text-decoration: none; text-align: center; }
button:hover, .button:hover { border-color: var(--accent); }
button:disabled { opacity: .45; cursor: default; }
:focus-visible { outline: 3px solid var(--accent); outline-offset: 3px; }
.primary { background: var(--accent); color: #fff; border-color: var(--accent); font-weight: 650; }
#editor { max-width: 1120px; margin: auto; padding: 28px 24px 48px; }
.brand { display: flex; align-items: center; gap: 10px; font-weight: 650; font-size: 15px; }
.brand img { width: 30px; height: 30px; }
h1 { margin: 28px 0 8px; font-size: clamp(28px, 4vw, 38px); letter-spacing: -.035em; }
h2 { margin: 0; font-size: 19px; }
p { line-height: 1.5; }
.intro { margin: 0 0 26px; color: var(--muted); }
.workspace { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; align-items: start; }
.section-title { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.hint { color: var(--muted); font-size: 14px; margin: 8px 0 16px; line-height: 1.5; }
#layers { list-style: none; padding: 0; margin: 0; }
.layer { padding: 16px; border: 1px solid var(--border); border-radius: 12px; background: var(--card); margin-bottom: 12px; }
.layer-header { display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
.layer-title { font-weight: 650; }
.layer-tools { display: flex; gap: 6px; }
.layer-tools button { padding: 7px 11px; font-size: 14px; }
.url-label { display: block; margin-bottom: 6px; font-size: 14px; font-weight: 600; }
.url-input, .size-input, textarea { width: 100%; min-width: 0; border: 1px solid var(--border); border-radius: 6px; padding: 11px; background: var(--card); color: var(--text); }
input[aria-invalid="true"] { border-color: var(--error); }
.size-input::placeholder { color: var(--muted); opacity: 1; }
.layer-layout { margin-top: 12px; border-top: 1px solid var(--border); padding-top: 12px; }
.layer-layout summary { cursor: pointer; min-height: 32px; font-weight: 600; font-size: 14px; }
.size-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
.size-fields label { display: block; font-size: 14px; }
.size-fields input { display: block; margin-top: 6px; }
.layout-reset { margin-top: 12px; font-size: 14px; }
.preview-size { margin-bottom: 16px; }
.layer-options { display: grid; gap: 4px; margin-top: 10px; }
.layer-options label { display: flex; gap: 9px; align-items: center; min-height: 38px; font-size: 14px; cursor: pointer; }
input[type="checkbox"] { width: 18px; height: 18px; accent-color: var(--accent); flex-shrink: 0; }
.actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 16px; }
#status { min-height: 24px; margin-bottom: 8px; overflow-wrap: anywhere; }
#status.error { color: var(--error); }
#link-panel { margin-top: 18px; }
#combined-url { min-height: 88px; resize: vertical; font-size: 14px; }
#qr-panel { margin-top: 18px; }
#qr-code { display: inline-block; background: #fff; line-height: 0; max-width: 100%; }
#qr-code canvas { display: block; max-width: 100%; height: auto; image-rendering: pixelated; }
.saved-setup { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; margin-top: 16px; }
.saved-setup .hint { margin: 0; }
#clear-saved { font-size: 14px; }
#preview { height: 292px; position: relative; overflow: hidden; border: 1px solid #3b4250; border-radius: 12px; background: #14161d; }
#preview-empty { color: #bfc7d6; text-align: center; padding: 30px; position: absolute; inset: 0; display: grid; place-content: center; line-height: 1.6; }
#preview-stage { position: absolute; top: 0; left: 0; transform-origin: top left; }
.overlay-frame { position: absolute; top: 0; left: 0; width: 100%; height: 100%; border: 0; background: transparent; color-scheme: normal; }
html.viewing { color-scheme: normal; }
.overlay-frame.pass-through { pointer-events: none; }
#output { position: fixed; inset: 0; overflow: hidden; background: transparent; }
.guide-link { margin-top: 28px; font-size: 14px; }
@media (max-width: 760px) {
#editor { padding: 20px 16px 32px; }
.workspace { grid-template-columns: minmax(0, 1fr); }
.actions > * { flex: 1; }
}
@media (prefers-color-scheme: dark) {
:root { color-scheme: dark; --page: #14161d; --card: #1b1e27; --text: #e9edf5; --muted: #afb9c9; --border: #444d5e; --accent: #b49aff; --error: #ffaba4; }
.primary { color: #1b1233; }
}
</style>
<script src="thirdparty/pako.min.js" defer></script>
<script src="thirdparty/qrcode.min.js" defer></script>
<script src="combine.js" defer></script>
</head>
<body>
<main id="editor" hidden>
<div class="brand"><img src="icons/logo.svg" alt="">Social Stream Ninja</div>
<h1>Combine overlays</h1>
<p class="intro">Chat and alerts in one tab. Paste your existing links, then copy one combined link.</p>
<div class="workspace">
<section aria-labelledby="layers-heading">
<div class="section-title"><h2 id="layers-heading">Your layers</h2><button id="add-layer" type="button">+ Add URL</button></div>
<p class="hint">Bottom layer first. Each URL after it appears on top.</p>
<ol id="layers"></ol>
<p class="hint">Start with chat below and alerts above. Leave taps off for alerts so you can scroll chat underneath.</p>
<div class="saved-setup">
<p id="save-status" class="hint" role="status">Links are saved in this browser as you edit.</p>
<button id="clear-saved" type="button">Clear saved setup</button>
</div>
<div class="actions">
<button id="copy-link" class="primary" type="button">Copy combined link</button>
<button id="show-qr" type="button">Show QR code</button>
<button id="preview-button" type="button">Preview</button>
</div>
<section id="qr-panel" aria-labelledby="qr-heading" hidden>
<h2 id="qr-heading">Open on your phone</h2>
<p class="hint">Scan with your phone’s camera. All your layers and settings are included.</p>
<div id="qr-code" role="img" aria-label="QR code for the combined view"></div>
<p id="qr-status" class="hint" role="status"></p>
</section>
<p id="status" role="status" aria-live="polite"></p>
<div id="link-panel" hidden>
<label class="url-label" for="combined-url">Combined link</label>
<textarea id="combined-url" readonly spellcheck="false"></textarea>
<div class="actions">
<a id="open-link" class="button" target="_blank" rel="noopener noreferrer">Open combined view</a>
<a id="edit-link" class="button" target="_blank" rel="noopener noreferrer">Open editable link</a>
</div>
</div>
</section>
<section aria-labelledby="preview-heading">
<div class="section-title"><h2 id="preview-heading">Preview</h2></div>
<div class="size-fields preview-size">
<label for="preview-width">Browser source width (px)<input id="preview-width" class="size-input" type="number" min="1" max="16384" step="1" value="1920"></label>
<label for="preview-height">Browser source height (px)<input id="preview-height" class="size-input" type="number" min="1" max="16384" step="1" value="1080"></label>
</div>
<p class="hint">Match your OBS Browser Source dimensions to preview placement. These fields only size the preview; the combined view fills its browser.</p>
<p class="hint"><a href="docs/multiple-overlays-guide.html#combine-obs" target="_blank" rel="noopener noreferrer">OBS layout example: left chat column, Hype above, and fullscreen overlays</a>. No CSS needed for placement.</p>
<div id="preview">
<div id="preview-empty">Your overlays will appear here.<br>Paste your links and select Preview.</div>
<div id="preview-stage"></div>
</div>
<p class="hint">The preview has a dark background. The combined view has no background or setup controls.</p>
<p class="hint">Transparency works with Dock, Featured Chat and Multi Alerts. Other pages use their own background settings and must allow embedding.</p>
</section>
</div>
<p class="guide-link"><a href="docs/multiple-overlays-guide.html#combine">Setup and troubleshooting</a></p>
</main>
<div id="output" hidden></div>
<noscript>This page needs JavaScript to combine your overlays.</noscript>
</body>
</html>