@commonpub/layer 0.21.22 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components/admin/theme/AdminThemeFamilyCard.vue +277 -0
- package/components/admin/theme/AdminThemeOverridesPanel.vue +222 -0
- package/components/admin/theme/AdminThemePreviewPane.vue +187 -0
- package/components/admin/theme/AdminThemeSceneAdmin.vue +189 -0
- package/components/admin/theme/AdminThemeSceneGallery.vue +353 -0
- package/components/admin/theme/AdminThemeSceneProse.vue +140 -0
- package/components/admin/theme/AdminThemeTokenGroup.vue +98 -0
- package/components/admin/theme/AdminThemeTokenInput.vue +278 -0
- package/composables/useTheme.ts +24 -14
- package/composables/useThemeAdmin.ts +167 -0
- package/package.json +7 -7
- package/pages/admin/theme/edit/[id].vue +547 -0
- package/pages/admin/theme/index.vue +424 -0
- package/plugins/theme.ts +25 -7
- package/server/api/admin/themes/[id].delete.ts +40 -0
- package/server/api/admin/themes/[id].get.ts +20 -0
- package/server/api/admin/themes/[id].put.ts +45 -0
- package/server/api/admin/themes/discover.get.ts +22 -0
- package/server/api/admin/themes/index.get.ts +40 -0
- package/server/api/admin/themes/index.post.ts +46 -0
- package/server/api/profile/theme.put.ts +2 -1
- package/server/middleware/theme.ts +23 -9
- package/server/utils/instanceTheme.ts +145 -25
- package/types/theme.ts +54 -0
- package/utils/themeDiscovery.ts +67 -0
- package/utils/themeIO.ts +79 -0
- package/utils/themeIds.ts +25 -0
- package/pages/admin/theme.vue +0 -502
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Admin-shell preview. Renders a fake topbar + sidebar + stat dashboard
|
|
4
|
+
* + table so admins can preview what the panel they're using right now
|
|
5
|
+
* will look like with the in-progress theme applied.
|
|
6
|
+
*/
|
|
7
|
+
</script>
|
|
8
|
+
|
|
9
|
+
<template>
|
|
10
|
+
<div class="scene-admin">
|
|
11
|
+
<header class="scene-admin-top">
|
|
12
|
+
<span class="scene-admin-brand">{{ '{ Instance Name }' }}</span>
|
|
13
|
+
<span class="scene-admin-badge">Admin</span>
|
|
14
|
+
<span class="scene-admin-back">← Back to site</span>
|
|
15
|
+
</header>
|
|
16
|
+
|
|
17
|
+
<div class="scene-admin-body">
|
|
18
|
+
<nav class="scene-admin-side">
|
|
19
|
+
<a class="scene-admin-link"><i class="fa-solid fa-gauge" aria-hidden="true" />Dashboard</a>
|
|
20
|
+
<a class="scene-admin-link"><i class="fa-solid fa-users" aria-hidden="true" />Users</a>
|
|
21
|
+
<a class="scene-admin-link"><i class="fa-solid fa-newspaper" aria-hidden="true" />Content</a>
|
|
22
|
+
<a class="scene-admin-link active"><i class="fa-solid fa-palette" aria-hidden="true" />Theme</a>
|
|
23
|
+
<a class="scene-admin-link"><i class="fa-solid fa-globe" aria-hidden="true" />Federation</a>
|
|
24
|
+
<a class="scene-admin-link"><i class="fa-solid fa-toggle-on" aria-hidden="true" />Features</a>
|
|
25
|
+
</nav>
|
|
26
|
+
|
|
27
|
+
<main class="scene-admin-main">
|
|
28
|
+
<h1 class="scene-admin-title">Instance overview</h1>
|
|
29
|
+
<p class="scene-admin-sub">Snapshot of activity across this CommonPub instance.</p>
|
|
30
|
+
|
|
31
|
+
<div class="scene-admin-stats">
|
|
32
|
+
<div class="scene-admin-stat">
|
|
33
|
+
<span class="scene-admin-stat-label">Members</span>
|
|
34
|
+
<span class="scene-admin-stat-value">3,124</span>
|
|
35
|
+
<span class="scene-admin-stat-delta">+18 this week</span>
|
|
36
|
+
</div>
|
|
37
|
+
<div class="scene-admin-stat">
|
|
38
|
+
<span class="scene-admin-stat-label">Published</span>
|
|
39
|
+
<span class="scene-admin-stat-value">512</span>
|
|
40
|
+
<span class="scene-admin-stat-delta">+24 this week</span>
|
|
41
|
+
</div>
|
|
42
|
+
<div class="scene-admin-stat">
|
|
43
|
+
<span class="scene-admin-stat-label">Federated</span>
|
|
44
|
+
<span class="scene-admin-stat-value">87 peers</span>
|
|
45
|
+
<span class="scene-admin-stat-delta tone-warn">2 retrying</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div class="scene-admin-stat">
|
|
48
|
+
<span class="scene-admin-stat-label">Storage</span>
|
|
49
|
+
<span class="scene-admin-stat-value">8.2 GB</span>
|
|
50
|
+
<span class="scene-admin-stat-delta">of 50 GB</span>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
|
|
54
|
+
<h2 class="scene-admin-h2">Recent reports</h2>
|
|
55
|
+
<div class="scene-admin-table-wrap">
|
|
56
|
+
<table class="scene-admin-table">
|
|
57
|
+
<thead>
|
|
58
|
+
<tr><th>Target</th><th>Reason</th><th>Reporter</th><th>Status</th></tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
<tr>
|
|
62
|
+
<td><strong>@spam-actor@example.fed</strong></td>
|
|
63
|
+
<td>Spam</td>
|
|
64
|
+
<td>moheeb</td>
|
|
65
|
+
<td><span class="scene-admin-pill tone-yellow">Open</span></td>
|
|
66
|
+
</tr>
|
|
67
|
+
<tr>
|
|
68
|
+
<td>Project: "Buy followers fast"</td>
|
|
69
|
+
<td>Spam</td>
|
|
70
|
+
<td>aly</td>
|
|
71
|
+
<td><span class="scene-admin-pill tone-green">Resolved</span></td>
|
|
72
|
+
</tr>
|
|
73
|
+
<tr>
|
|
74
|
+
<td>Comment on "Edge AI nodes"</td>
|
|
75
|
+
<td>Harassment</td>
|
|
76
|
+
<td>kim</td>
|
|
77
|
+
<td><span class="scene-admin-pill tone-red">Escalated</span></td>
|
|
78
|
+
</tr>
|
|
79
|
+
</tbody>
|
|
80
|
+
</table>
|
|
81
|
+
</div>
|
|
82
|
+
</main>
|
|
83
|
+
</div>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<style scoped>
|
|
88
|
+
.scene-admin {
|
|
89
|
+
background: var(--bg);
|
|
90
|
+
border: var(--border-width-default) solid var(--border);
|
|
91
|
+
min-height: 480px;
|
|
92
|
+
overflow: hidden;
|
|
93
|
+
display: flex;
|
|
94
|
+
flex-direction: column;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.scene-admin-top {
|
|
98
|
+
height: 44px;
|
|
99
|
+
display: flex;
|
|
100
|
+
align-items: center;
|
|
101
|
+
gap: var(--space-3);
|
|
102
|
+
padding: 0 var(--space-4);
|
|
103
|
+
background: var(--surface);
|
|
104
|
+
border-bottom: var(--border-width-default) solid var(--border);
|
|
105
|
+
}
|
|
106
|
+
.scene-admin-brand { font-weight: var(--font-weight-bold); color: var(--text); }
|
|
107
|
+
.scene-admin-badge {
|
|
108
|
+
padding: 2px 8px;
|
|
109
|
+
background: var(--accent);
|
|
110
|
+
color: var(--color-on-accent);
|
|
111
|
+
font-family: var(--font-mono);
|
|
112
|
+
font-size: 10px;
|
|
113
|
+
letter-spacing: var(--tracking-wide);
|
|
114
|
+
text-transform: uppercase;
|
|
115
|
+
}
|
|
116
|
+
.scene-admin-back { margin-left: auto; color: var(--text-dim); font-size: var(--text-sm); }
|
|
117
|
+
|
|
118
|
+
.scene-admin-body { display: flex; flex: 1; min-height: 0; }
|
|
119
|
+
.scene-admin-side {
|
|
120
|
+
width: 180px;
|
|
121
|
+
padding: var(--space-3) var(--space-2);
|
|
122
|
+
background: var(--surface);
|
|
123
|
+
border-right: var(--border-width-default) solid var(--border);
|
|
124
|
+
display: flex;
|
|
125
|
+
flex-direction: column;
|
|
126
|
+
gap: 2px;
|
|
127
|
+
}
|
|
128
|
+
.scene-admin-link {
|
|
129
|
+
display: flex;
|
|
130
|
+
align-items: center;
|
|
131
|
+
gap: 10px;
|
|
132
|
+
padding: 6px var(--space-3);
|
|
133
|
+
color: var(--text-dim);
|
|
134
|
+
font-size: var(--text-sm);
|
|
135
|
+
cursor: pointer;
|
|
136
|
+
text-decoration: none;
|
|
137
|
+
}
|
|
138
|
+
.scene-admin-link i { width: 14px; text-align: center; font-size: 11px; }
|
|
139
|
+
.scene-admin-link:hover { color: var(--text); background: var(--surface2); }
|
|
140
|
+
.scene-admin-link.active { color: var(--accent); background: var(--accent-bg); font-weight: var(--font-weight-semibold); }
|
|
141
|
+
|
|
142
|
+
.scene-admin-main { flex: 1; padding: var(--space-5); min-width: 0; }
|
|
143
|
+
.scene-admin-title { font-size: var(--text-xl); font-weight: var(--font-weight-bold); color: var(--text); margin: 0 0 var(--space-1); }
|
|
144
|
+
.scene-admin-sub { color: var(--text-dim); margin: 0 0 var(--space-5); }
|
|
145
|
+
|
|
146
|
+
.scene-admin-stats {
|
|
147
|
+
display: grid;
|
|
148
|
+
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
|
149
|
+
gap: var(--space-3);
|
|
150
|
+
margin-bottom: var(--space-6);
|
|
151
|
+
}
|
|
152
|
+
.scene-admin-stat {
|
|
153
|
+
background: var(--surface);
|
|
154
|
+
border: var(--border-width-default) solid var(--border);
|
|
155
|
+
padding: var(--space-3) var(--space-4);
|
|
156
|
+
display: flex;
|
|
157
|
+
flex-direction: column;
|
|
158
|
+
gap: 2px;
|
|
159
|
+
box-shadow: var(--shadow-sm);
|
|
160
|
+
}
|
|
161
|
+
.scene-admin-stat-label { font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim); }
|
|
162
|
+
.scene-admin-stat-value { font-size: var(--text-xl); font-weight: var(--font-weight-bold); color: var(--text); }
|
|
163
|
+
.scene-admin-stat-delta { font-size: var(--text-sm); color: var(--green); }
|
|
164
|
+
.scene-admin-stat-delta.tone-warn { color: var(--yellow); }
|
|
165
|
+
|
|
166
|
+
.scene-admin-h2 { font-size: var(--text-md); font-weight: var(--font-weight-semibold); color: var(--text); margin: 0 0 var(--space-2); }
|
|
167
|
+
.scene-admin-table-wrap { background: var(--surface); border: var(--border-width-default) solid var(--border); overflow: hidden; }
|
|
168
|
+
.scene-admin-table { width: 100%; border-collapse: collapse; }
|
|
169
|
+
.scene-admin-table th, .scene-admin-table td { padding: var(--space-2) var(--space-3); border-bottom: var(--border-width-thin) solid var(--border2); text-align: left; font-size: var(--text-sm); color: var(--text); }
|
|
170
|
+
.scene-admin-table th { font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim); background: var(--surface2); }
|
|
171
|
+
.scene-admin-table tr:last-child td { border-bottom: 0; }
|
|
172
|
+
.scene-admin-pill {
|
|
173
|
+
display: inline-block;
|
|
174
|
+
padding: 1px 8px;
|
|
175
|
+
font-family: var(--font-mono);
|
|
176
|
+
font-size: 10px;
|
|
177
|
+
letter-spacing: var(--tracking-wide);
|
|
178
|
+
text-transform: uppercase;
|
|
179
|
+
border: var(--border-width-thin) solid var(--border2);
|
|
180
|
+
}
|
|
181
|
+
.scene-admin-pill.tone-green { color: var(--green); background: var(--green-bg); border-color: var(--green-border); }
|
|
182
|
+
.scene-admin-pill.tone-yellow { color: var(--yellow); background: var(--yellow-bg); border-color: var(--yellow-border); }
|
|
183
|
+
.scene-admin-pill.tone-red { color: var(--red); background: var(--red-bg); border-color: var(--red-border); }
|
|
184
|
+
|
|
185
|
+
@media (max-width: 540px) {
|
|
186
|
+
.scene-admin-side { width: 50px; }
|
|
187
|
+
.scene-admin-link span { display: none; }
|
|
188
|
+
}
|
|
189
|
+
</style>
|
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* Component gallery preview. Renders representative UI primitives —
|
|
4
|
+
* buttons, cards, forms, badges, alerts, code — so every category of
|
|
5
|
+
* token has something visible to land on. Self-contained: all markup
|
|
6
|
+
* uses `var(--*)` only so the parent's inline-style scope drives the look.
|
|
7
|
+
*
|
|
8
|
+
* Designed to be REPLACEABLE later — when the layout-builder lands, this
|
|
9
|
+
* scene becomes one option among many in AdminThemePreviewPane's picker.
|
|
10
|
+
*/
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div class="scene-gallery">
|
|
15
|
+
<!-- TYPOGRAPHY -->
|
|
16
|
+
<section class="scene-block">
|
|
17
|
+
<h2 class="scene-section-label">Typography</h2>
|
|
18
|
+
<div class="scene-card">
|
|
19
|
+
<h1 class="scene-h1">Display heading H1</h1>
|
|
20
|
+
<h2 class="scene-h2">Section heading H2</h2>
|
|
21
|
+
<h3 class="scene-h3">Subsection heading H3</h3>
|
|
22
|
+
<p class="scene-body">
|
|
23
|
+
Body copy uses <code class="scene-inline-code">var(--font-body)</code> at
|
|
24
|
+
<code class="scene-inline-code">var(--text-base)</code>. The quick brown fox
|
|
25
|
+
jumps over the lazy dog.
|
|
26
|
+
</p>
|
|
27
|
+
<p class="scene-muted">Secondary text — note the contrast against background.</p>
|
|
28
|
+
<p class="scene-faint">Tertiary text — used for placeholders and faint metadata.</p>
|
|
29
|
+
<p class="scene-mono-label">Mono label — uppercase letter-spaced</p>
|
|
30
|
+
</div>
|
|
31
|
+
</section>
|
|
32
|
+
|
|
33
|
+
<!-- BUTTONS -->
|
|
34
|
+
<section class="scene-block">
|
|
35
|
+
<h2 class="scene-section-label">Buttons</h2>
|
|
36
|
+
<div class="scene-card scene-card-row">
|
|
37
|
+
<button class="scene-btn scene-btn-primary">Primary action</button>
|
|
38
|
+
<button class="scene-btn">Secondary</button>
|
|
39
|
+
<button class="scene-btn scene-btn-ghost">Ghost</button>
|
|
40
|
+
<button class="scene-btn scene-btn-danger">Destructive</button>
|
|
41
|
+
<button class="scene-btn" disabled>Disabled</button>
|
|
42
|
+
</div>
|
|
43
|
+
</section>
|
|
44
|
+
|
|
45
|
+
<!-- BADGES + SEMANTIC -->
|
|
46
|
+
<section class="scene-block">
|
|
47
|
+
<h2 class="scene-section-label">Badges & semantic colors</h2>
|
|
48
|
+
<div class="scene-card scene-card-row">
|
|
49
|
+
<span class="scene-badge tone-accent">Accent</span>
|
|
50
|
+
<span class="scene-badge tone-green">Success</span>
|
|
51
|
+
<span class="scene-badge tone-yellow">Warning</span>
|
|
52
|
+
<span class="scene-badge tone-red">Error</span>
|
|
53
|
+
<span class="scene-badge tone-purple">Info</span>
|
|
54
|
+
<span class="scene-badge tone-teal">Teal</span>
|
|
55
|
+
<span class="scene-badge tone-pink">Pink</span>
|
|
56
|
+
</div>
|
|
57
|
+
</section>
|
|
58
|
+
|
|
59
|
+
<!-- FORMS -->
|
|
60
|
+
<section class="scene-block">
|
|
61
|
+
<h2 class="scene-section-label">Forms</h2>
|
|
62
|
+
<div class="scene-card">
|
|
63
|
+
<label class="scene-field">
|
|
64
|
+
<span class="scene-field-label">Email address</span>
|
|
65
|
+
<input class="scene-input" type="email" placeholder="you@example.com" value="federation@deveco.io" />
|
|
66
|
+
</label>
|
|
67
|
+
<label class="scene-field">
|
|
68
|
+
<span class="scene-field-label">Bio</span>
|
|
69
|
+
<textarea class="scene-input scene-textarea" rows="3">Building open community tooling.</textarea>
|
|
70
|
+
</label>
|
|
71
|
+
<label class="scene-field">
|
|
72
|
+
<span class="scene-field-label">Visibility</span>
|
|
73
|
+
<select class="scene-input">
|
|
74
|
+
<option>Public</option>
|
|
75
|
+
<option>Members only</option>
|
|
76
|
+
<option>Private</option>
|
|
77
|
+
</select>
|
|
78
|
+
</label>
|
|
79
|
+
<div class="scene-checkrow">
|
|
80
|
+
<label class="scene-check"><input type="checkbox" checked /> Allow follows</label>
|
|
81
|
+
<label class="scene-check"><input type="checkbox" /> Email digest</label>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</section>
|
|
85
|
+
|
|
86
|
+
<!-- CONTENT CARDS -->
|
|
87
|
+
<section class="scene-block">
|
|
88
|
+
<h2 class="scene-section-label">Content cards</h2>
|
|
89
|
+
<div class="scene-card-grid">
|
|
90
|
+
<article class="scene-content-card">
|
|
91
|
+
<div class="scene-content-image" aria-hidden="true">
|
|
92
|
+
<span class="scene-content-type">PROJECT</span>
|
|
93
|
+
</div>
|
|
94
|
+
<div class="scene-content-body">
|
|
95
|
+
<h3 class="scene-content-title">Tiny mesh router for off-grid relays</h3>
|
|
96
|
+
<p class="scene-content-excerpt">Solar-powered LoRa nodes that auto-form a mesh and route AP federation activity over store-and-forward links.</p>
|
|
97
|
+
<div class="scene-content-meta">
|
|
98
|
+
<span class="scene-avatar" aria-hidden="true">M</span>
|
|
99
|
+
<span class="scene-content-author">moheeb</span>
|
|
100
|
+
<span class="scene-content-dot">·</span>
|
|
101
|
+
<span class="scene-content-date">3 days ago</span>
|
|
102
|
+
</div>
|
|
103
|
+
</div>
|
|
104
|
+
</article>
|
|
105
|
+
<article class="scene-content-card">
|
|
106
|
+
<div class="scene-content-image scene-content-image-alt" aria-hidden="true">
|
|
107
|
+
<span class="scene-content-type tone-explainer">EXPLAINER</span>
|
|
108
|
+
</div>
|
|
109
|
+
<div class="scene-content-body">
|
|
110
|
+
<h3 class="scene-content-title">How ActivityPub delivery actually works</h3>
|
|
111
|
+
<p class="scene-content-excerpt">An interactive walkthrough of the inbox / outbox dance, signatures, and the recovery dead-letter queue.</p>
|
|
112
|
+
<div class="scene-content-meta">
|
|
113
|
+
<span class="scene-avatar" aria-hidden="true">C</span>
|
|
114
|
+
<span class="scene-content-author">commonpub</span>
|
|
115
|
+
<span class="scene-content-dot">·</span>
|
|
116
|
+
<span class="scene-content-date">Last week</span>
|
|
117
|
+
</div>
|
|
118
|
+
</div>
|
|
119
|
+
</article>
|
|
120
|
+
</div>
|
|
121
|
+
</section>
|
|
122
|
+
|
|
123
|
+
<!-- ALERTS -->
|
|
124
|
+
<section class="scene-block">
|
|
125
|
+
<h2 class="scene-section-label">Alerts</h2>
|
|
126
|
+
<div class="scene-stack">
|
|
127
|
+
<div class="scene-alert tone-accent">
|
|
128
|
+
<i class="fa-solid fa-circle-info" aria-hidden="true" />
|
|
129
|
+
<div>
|
|
130
|
+
<strong>Federation enabled.</strong>
|
|
131
|
+
Your instance is sending and receiving ActivityPub messages with 3 peers.
|
|
132
|
+
</div>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="scene-alert tone-green">
|
|
135
|
+
<i class="fa-solid fa-check" aria-hidden="true" />
|
|
136
|
+
<div><strong>Saved.</strong> Theme tokens are applied to your live site.</div>
|
|
137
|
+
</div>
|
|
138
|
+
<div class="scene-alert tone-yellow">
|
|
139
|
+
<i class="fa-solid fa-triangle-exclamation" aria-hidden="true" />
|
|
140
|
+
<div><strong>Heads up:</strong> two tokens reference colors that fail WCAG AA contrast.</div>
|
|
141
|
+
</div>
|
|
142
|
+
</div>
|
|
143
|
+
</section>
|
|
144
|
+
|
|
145
|
+
<!-- CODE -->
|
|
146
|
+
<section class="scene-block">
|
|
147
|
+
<h2 class="scene-section-label">Code block</h2>
|
|
148
|
+
<div class="scene-code">
|
|
149
|
+
<div class="scene-code-header">
|
|
150
|
+
<span>useFederationDigest.ts</span>
|
|
151
|
+
<button class="scene-code-copy" type="button">copy</button>
|
|
152
|
+
</div>
|
|
153
|
+
<pre class="scene-code-body"><code>// Computes the rfc-3230 digest header for an inbox POST.
|
|
154
|
+
export function digestFor(body: string): string {
|
|
155
|
+
const buf = new TextEncoder().encode(body);
|
|
156
|
+
const hash = crypto.subtle.digestSync('SHA-256', buf);
|
|
157
|
+
return 'SHA-256=' + base64(hash);
|
|
158
|
+
}</code></pre>
|
|
159
|
+
</div>
|
|
160
|
+
</section>
|
|
161
|
+
|
|
162
|
+
<!-- SHADOWS / SHAPES -->
|
|
163
|
+
<section class="scene-block">
|
|
164
|
+
<h2 class="scene-section-label">Shadows & shape</h2>
|
|
165
|
+
<div class="scene-shadow-grid">
|
|
166
|
+
<div class="scene-shadow-box" style="box-shadow: var(--shadow-sm)">sm</div>
|
|
167
|
+
<div class="scene-shadow-box" style="box-shadow: var(--shadow-md)">md</div>
|
|
168
|
+
<div class="scene-shadow-box" style="box-shadow: var(--shadow-lg)">lg</div>
|
|
169
|
+
<div class="scene-shadow-box" style="box-shadow: var(--shadow-xl)">xl</div>
|
|
170
|
+
<div class="scene-shadow-box" style="box-shadow: var(--shadow-accent)">accent</div>
|
|
171
|
+
</div>
|
|
172
|
+
</section>
|
|
173
|
+
</div>
|
|
174
|
+
</template>
|
|
175
|
+
|
|
176
|
+
<style scoped>
|
|
177
|
+
.scene-gallery { display: flex; flex-direction: column; gap: var(--space-6); max-width: 720px; margin: 0 auto; }
|
|
178
|
+
|
|
179
|
+
.scene-block { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
180
|
+
|
|
181
|
+
.scene-section-label {
|
|
182
|
+
font-family: var(--font-mono);
|
|
183
|
+
font-size: var(--text-label);
|
|
184
|
+
letter-spacing: var(--tracking-wide);
|
|
185
|
+
text-transform: uppercase;
|
|
186
|
+
color: var(--text-dim);
|
|
187
|
+
margin: 0;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.scene-card {
|
|
191
|
+
background: var(--surface);
|
|
192
|
+
border: var(--border-width-default) solid var(--border);
|
|
193
|
+
padding: var(--space-4);
|
|
194
|
+
display: flex;
|
|
195
|
+
flex-direction: column;
|
|
196
|
+
gap: var(--space-3);
|
|
197
|
+
}
|
|
198
|
+
.scene-card-row { flex-direction: row; flex-wrap: wrap; align-items: center; }
|
|
199
|
+
|
|
200
|
+
/* Typography */
|
|
201
|
+
.scene-h1 { font-family: var(--font-heading); font-size: var(--text-3xl); font-weight: var(--font-weight-bold); margin: 0; color: var(--text); letter-spacing: var(--tracking-tight); line-height: var(--leading-tight); }
|
|
202
|
+
.scene-h2 { font-family: var(--font-heading); font-size: var(--text-xl); font-weight: var(--font-weight-semibold); margin: 0; color: var(--text); }
|
|
203
|
+
.scene-h3 { font-family: var(--font-heading); font-size: var(--text-md); font-weight: var(--font-weight-semibold); margin: 0; color: var(--text); }
|
|
204
|
+
.scene-body { font-size: var(--text-base); line-height: var(--leading-normal); color: var(--text); margin: 0; }
|
|
205
|
+
.scene-muted { font-size: var(--text-sm); color: var(--text-dim); margin: 0; }
|
|
206
|
+
.scene-faint { font-size: var(--text-sm); color: var(--text-faint); margin: 0; }
|
|
207
|
+
.scene-mono-label { font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim); margin: 0; }
|
|
208
|
+
.scene-inline-code { font-family: var(--font-mono); font-size: 0.9em; padding: 1px 6px; background: var(--surface2); color: var(--accent); border: var(--border-width-thin) solid var(--border2); }
|
|
209
|
+
|
|
210
|
+
/* Buttons */
|
|
211
|
+
.scene-btn {
|
|
212
|
+
display: inline-flex;
|
|
213
|
+
align-items: center;
|
|
214
|
+
gap: 6px;
|
|
215
|
+
padding: 8px 14px;
|
|
216
|
+
font-family: var(--font-mono);
|
|
217
|
+
font-size: var(--text-sm);
|
|
218
|
+
font-weight: var(--font-weight-semibold);
|
|
219
|
+
letter-spacing: var(--tracking-wide);
|
|
220
|
+
background: var(--surface);
|
|
221
|
+
color: var(--text);
|
|
222
|
+
border: var(--border-width-default) solid var(--border);
|
|
223
|
+
cursor: pointer;
|
|
224
|
+
box-shadow: var(--shadow-sm);
|
|
225
|
+
transition: transform var(--transition-fast);
|
|
226
|
+
}
|
|
227
|
+
.scene-btn:hover { transform: translate(-1px, -1px); box-shadow: var(--shadow-md); }
|
|
228
|
+
.scene-btn:active { transform: translate(0, 0); box-shadow: none; }
|
|
229
|
+
.scene-btn:disabled { opacity: 0.55; cursor: not-allowed; box-shadow: none; transform: none; }
|
|
230
|
+
.scene-btn-primary { background: var(--accent); color: var(--color-on-accent); border-color: var(--border); }
|
|
231
|
+
.scene-btn-ghost { background: transparent; box-shadow: none; }
|
|
232
|
+
.scene-btn-danger { background: var(--red); color: var(--color-text-inverse); border-color: var(--border); }
|
|
233
|
+
|
|
234
|
+
/* Badges */
|
|
235
|
+
.scene-badge {
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
gap: 4px;
|
|
239
|
+
padding: 2px 8px;
|
|
240
|
+
font-family: var(--font-mono);
|
|
241
|
+
font-size: var(--text-label);
|
|
242
|
+
letter-spacing: var(--tracking-wide);
|
|
243
|
+
text-transform: uppercase;
|
|
244
|
+
border: var(--border-width-thin) solid var(--border2);
|
|
245
|
+
}
|
|
246
|
+
.tone-accent { color: var(--accent); background: var(--accent-bg); border-color: var(--accent-border); }
|
|
247
|
+
.tone-green { color: var(--green); background: var(--green-bg); border-color: var(--green-border); }
|
|
248
|
+
.tone-yellow { color: var(--yellow); background: var(--yellow-bg); border-color: var(--yellow-border); }
|
|
249
|
+
.tone-red { color: var(--red); background: var(--red-bg); border-color: var(--red-border); }
|
|
250
|
+
.tone-purple { color: var(--purple); background: var(--purple-bg); border-color: var(--purple-border); }
|
|
251
|
+
.tone-teal { color: var(--teal); background: var(--teal-bg); border-color: var(--teal-border); }
|
|
252
|
+
.tone-pink { color: var(--pink); background: var(--pink-bg); border-color: var(--pink-border); }
|
|
253
|
+
.tone-explainer { color: var(--teal); background: var(--teal-bg); border-color: var(--teal-border); }
|
|
254
|
+
|
|
255
|
+
/* Forms */
|
|
256
|
+
.scene-field { display: flex; flex-direction: column; gap: 6px; }
|
|
257
|
+
.scene-field-label { font-family: var(--font-mono); font-size: var(--text-label); letter-spacing: var(--tracking-wide); text-transform: uppercase; color: var(--text-dim); }
|
|
258
|
+
.scene-input {
|
|
259
|
+
background: var(--surface2);
|
|
260
|
+
color: var(--text);
|
|
261
|
+
border: var(--border-width-default) solid var(--border);
|
|
262
|
+
padding: 8px 10px;
|
|
263
|
+
font-size: var(--text-sm);
|
|
264
|
+
font-family: var(--font-body);
|
|
265
|
+
width: 100%;
|
|
266
|
+
}
|
|
267
|
+
.scene-input:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
268
|
+
.scene-textarea { resize: vertical; min-height: 60px; font-family: var(--font-body); }
|
|
269
|
+
.scene-checkrow { display: flex; gap: var(--space-4); flex-wrap: wrap; }
|
|
270
|
+
.scene-check { display: inline-flex; align-items: center; gap: 6px; font-size: var(--text-sm); color: var(--text); }
|
|
271
|
+
|
|
272
|
+
/* Content cards */
|
|
273
|
+
.scene-card-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--space-4); }
|
|
274
|
+
.scene-content-card { background: var(--surface); border: var(--border-width-default) solid var(--border); display: flex; flex-direction: column; box-shadow: var(--shadow-sm); }
|
|
275
|
+
.scene-content-image { position: relative; height: 120px; background: linear-gradient(135deg, var(--accent-bg) 0%, var(--accent-bg-strong, var(--accent-bg)) 100%); border-bottom: var(--border-width-default) solid var(--border); }
|
|
276
|
+
.scene-content-image-alt { background: linear-gradient(135deg, var(--teal-bg) 0%, var(--purple-bg) 100%); }
|
|
277
|
+
.scene-content-type { position: absolute; top: 8px; left: 8px; font-family: var(--font-mono); font-size: 10px; letter-spacing: var(--tracking-wide); padding: 2px 6px; background: var(--color-badge-overlay, rgba(0,0,0,0.7)); color: #fff; }
|
|
278
|
+
.scene-content-body { padding: var(--space-4); display: flex; flex-direction: column; gap: var(--space-2); }
|
|
279
|
+
.scene-content-title { font-size: var(--text-md); font-weight: var(--font-weight-semibold); margin: 0; color: var(--text); }
|
|
280
|
+
.scene-content-excerpt { font-size: var(--text-sm); color: var(--text-dim); margin: 0; line-height: var(--leading-snug); }
|
|
281
|
+
.scene-content-meta { display: flex; align-items: center; gap: 6px; font-size: var(--text-sm); color: var(--text-dim); }
|
|
282
|
+
.scene-avatar { width: 22px; height: 22px; border-radius: var(--radius-full); background: var(--accent); color: var(--color-on-accent); display: inline-flex; align-items: center; justify-content: center; font-size: 11px; font-weight: var(--font-weight-bold); }
|
|
283
|
+
.scene-content-author { color: var(--text); font-weight: var(--font-weight-medium); }
|
|
284
|
+
.scene-content-dot { color: var(--text-faint); }
|
|
285
|
+
|
|
286
|
+
/* Alerts */
|
|
287
|
+
.scene-stack { display: flex; flex-direction: column; gap: var(--space-2); }
|
|
288
|
+
.scene-alert {
|
|
289
|
+
display: flex;
|
|
290
|
+
gap: var(--space-3);
|
|
291
|
+
padding: var(--space-3) var(--space-4);
|
|
292
|
+
background: var(--surface);
|
|
293
|
+
border: var(--border-width-default) solid var(--border);
|
|
294
|
+
border-left-width: 4px;
|
|
295
|
+
font-size: var(--text-sm);
|
|
296
|
+
color: var(--text);
|
|
297
|
+
}
|
|
298
|
+
.scene-alert i { padding-top: 2px; flex-shrink: 0; }
|
|
299
|
+
.scene-alert.tone-accent { border-left-color: var(--accent); background: var(--accent-bg); }
|
|
300
|
+
.scene-alert.tone-accent i { color: var(--accent); }
|
|
301
|
+
.scene-alert.tone-green { border-left-color: var(--green); background: var(--green-bg); }
|
|
302
|
+
.scene-alert.tone-green i { color: var(--green); }
|
|
303
|
+
.scene-alert.tone-yellow { border-left-color: var(--yellow); background: var(--yellow-bg); }
|
|
304
|
+
.scene-alert.tone-yellow i { color: var(--yellow); }
|
|
305
|
+
|
|
306
|
+
/* Code */
|
|
307
|
+
.scene-code {
|
|
308
|
+
border: var(--border-width-default) solid var(--code-border, var(--border));
|
|
309
|
+
background: var(--code-bg);
|
|
310
|
+
color: var(--code-text);
|
|
311
|
+
font-family: var(--font-mono);
|
|
312
|
+
overflow: hidden;
|
|
313
|
+
border-radius: 0;
|
|
314
|
+
}
|
|
315
|
+
.scene-code-header {
|
|
316
|
+
display: flex;
|
|
317
|
+
justify-content: space-between;
|
|
318
|
+
align-items: center;
|
|
319
|
+
padding: var(--space-2) var(--space-3);
|
|
320
|
+
background: var(--code-header-bg);
|
|
321
|
+
color: var(--code-muted);
|
|
322
|
+
font-size: var(--text-label);
|
|
323
|
+
letter-spacing: var(--tracking-wide);
|
|
324
|
+
text-transform: uppercase;
|
|
325
|
+
border-bottom: var(--border-width-thin) solid var(--code-border, var(--border));
|
|
326
|
+
}
|
|
327
|
+
.scene-code-copy {
|
|
328
|
+
background: none;
|
|
329
|
+
border: var(--border-width-thin) solid var(--code-border, var(--border));
|
|
330
|
+
color: var(--code-muted);
|
|
331
|
+
font-family: var(--font-mono);
|
|
332
|
+
font-size: var(--text-label);
|
|
333
|
+
padding: 2px 8px;
|
|
334
|
+
cursor: pointer;
|
|
335
|
+
}
|
|
336
|
+
.scene-code-body { margin: 0; padding: var(--space-3) var(--space-4); font-size: var(--text-sm); white-space: pre-wrap; overflow: auto; }
|
|
337
|
+
|
|
338
|
+
/* Shadows */
|
|
339
|
+
.scene-shadow-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: var(--space-6); padding: var(--space-4); }
|
|
340
|
+
.scene-shadow-box {
|
|
341
|
+
height: 60px;
|
|
342
|
+
background: var(--surface);
|
|
343
|
+
border: var(--border-width-default) solid var(--border);
|
|
344
|
+
display: flex;
|
|
345
|
+
align-items: center;
|
|
346
|
+
justify-content: center;
|
|
347
|
+
font-family: var(--font-mono);
|
|
348
|
+
font-size: var(--text-label);
|
|
349
|
+
text-transform: uppercase;
|
|
350
|
+
letter-spacing: var(--tracking-wide);
|
|
351
|
+
color: var(--text-dim);
|
|
352
|
+
}
|
|
353
|
+
</style>
|