collavre 0.12.0 → 0.12.2

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfb336e9ba2c33ae2bebe02cb641ca55078e8a9e7b7534a2296fb9fe344e8ab8
4
- data.tar.gz: 92446cc1f6e94e96c85bb3d623675671b3a1c918dbe18f365ea4cadcf9844a8f
3
+ metadata.gz: e5cc8288417aa35fba130bae92aeb47383ba3f53dd10fb3e9d1d46d65605fbf2
4
+ data.tar.gz: 7862466adcb4e962f1a0fb5aa62f3958cafe98469df3da543d3df9d12b2c6bc7
5
5
  SHA512:
6
- metadata.gz: ef40b534cba46f348398f4097be26d8398dade4008cbef542fb8faaccebe5a7bc2647ba3666e5020cf1e32d29e8c9e4d37d877036c6b443c67247735221e68c0
7
- data.tar.gz: 5dd19bdf5053beea83aeba18edc324df6fc86f429abf4f3e2a160d4728cc4c35a638adaace885d87855d0e5f71c8c578aa8280877126fada04b2e9ec29d0ca3b
6
+ metadata.gz: fd4d1f51f6dd4044b1d5d7c09f06437dc4dd59962c2632486794a4dcfc2eb8baa01dd8ecd6ece69b54d9296bf8a1dc3a237f190ccd02b75c54a5074fe89b09d7
7
+ data.tar.gz: 5ec2172df49895ba3de6063f18373c52cefb8e475f829590e46a086b6b86a298782862936da30925bcd574fff8642fc5ba7ebba28ea7f68f2147fed27a0aa800
@@ -56,14 +56,48 @@
56
56
  top: calc(100% + 0.35rem);
57
57
  left: 0;
58
58
  z-index: 10;
59
- display: inline-flex;
60
- align-items: center;
61
- gap: 0.35rem;
62
- padding: 0.4rem 0.5rem;
63
- background: var(--surface-bg);
59
+ display: flex;
60
+ flex-direction: column;
61
+ gap: var(--space-2);
62
+ padding: var(--space-2);
63
+ background: var(--surface-section);
64
64
  border: 1px solid var(--border-color);
65
65
  border-radius: var(--radius-2);
66
66
  box-shadow: var(--shadow-3);
67
+ min-width: 180px;
68
+ }
69
+
70
+ .lexical-toolbar-color__tokens {
71
+ display: grid;
72
+ grid-template-columns: repeat(4, 1fr);
73
+ gap: var(--space-1);
74
+ }
75
+
76
+ .lexical-toolbar-color__token-btn {
77
+ width: 28px;
78
+ height: 28px;
79
+ border: 1px solid var(--border-color);
80
+ border-radius: var(--radius-2);
81
+ cursor: pointer;
82
+ padding: 0;
83
+ transition: transform 0.1s var(--ease-out-2);
84
+ }
85
+
86
+ .lexical-toolbar-color__token-btn:hover {
87
+ transform: scale(1.15);
88
+ border-color: var(--color-active);
89
+ }
90
+
91
+ .lexical-toolbar-color__token-btn.active {
92
+ box-shadow: 0 0 0 2px var(--color-active);
93
+ }
94
+
95
+ .lexical-toolbar-color__custom-row {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: var(--space-1);
99
+ border-top: 1px solid var(--border-color);
100
+ padding-top: var(--space-2);
67
101
  }
68
102
 
69
103
  .lexical-toolbar-color__popover input[type="color"] {
@@ -74,6 +108,7 @@
74
108
  height: var(--space-7);
75
109
  padding: 0;
76
110
  background: transparent;
111
+ flex-shrink: 0;
77
112
  }
78
113
 
79
114
  .lexical-toolbar-btn--small {
@@ -621,6 +621,11 @@ creative-tree-row.chat-active .creative-row {
621
621
  font-size: 1.4em;
622
622
  }
623
623
 
624
+ /* ── Title text: larger than default h1 ── */
625
+ .creative-tree-title .page-title {
626
+ font-size: var(--text-5, 2rem);
627
+ }
628
+
624
629
 
625
630
  /* Constrain images within creative-row to prevent overflow */
626
631
  .creative-row img {
@@ -251,14 +251,16 @@ input[type="datetime-local"]::-webkit-datetime-edit-minute-field {
251
251
  display: inline-flex;
252
252
  align-items: center;
253
253
  justify-content: center;
254
- min-width: 1.2em;
255
- padding: 0.05em 0.35em;
254
+ min-width: var(--space-3);
255
+ height: var(--space-3);
256
+ padding: 0 0.3em;
256
257
  font-size: 0.65rem;
257
258
  font-weight: 600;
258
259
  line-height: 1;
259
260
  border-radius: var(--radius-round, 9999px);
260
261
  background: var(--color-badge-bg);
261
262
  color: var(--text-on-badge);
263
+ box-sizing: border-box;
262
264
  }
263
265
 
264
266
  .badge-success {
@@ -113,14 +113,18 @@ nav .popup-menu-item a:hover {
113
113
  background: var(--color-badge-bg, red);
114
114
  color: var(--color-badge-text, white);
115
115
  border-radius: var(--radius-round);
116
- padding: 0 6px;
117
116
  font-size: var(--text-00);
118
117
  font-weight: var(--weight-6);
119
- line-height: 1.4;
118
+ line-height: 1;
120
119
  margin-left: var(--space-1);
121
120
  display: none;
122
- min-width: 1.2em;
123
- text-align: center;
121
+ min-width: var(--space-3);
122
+ height: var(--space-3);
123
+ padding: 0 0.3em;
124
+ align-items: center;
125
+ justify-content: center;
126
+ box-sizing: border-box;
127
+ vertical-align: middle;
124
128
  }
125
129
 
126
130
  /* Responsive visibility helpers */
@@ -273,10 +273,40 @@ function CodeHighlightingPlugin() {
273
273
  return null
274
274
  }
275
275
 
276
- function ToolbarColorPicker({ icon, title, color, onChange, onClear }) {
276
+ const EDITOR_TEXT_TOKENS = [
277
+ { token: "var(--text-primary)", label: "Primary" },
278
+ { token: "var(--text-muted)", label: "Muted" },
279
+ { token: "var(--color-danger)", label: "Danger" },
280
+ { token: "var(--color-warning)", label: "Warning" },
281
+ { token: "var(--color-brand)", label: "Brand" },
282
+ { token: "var(--color-link)", label: "Link" },
283
+ { token: "var(--color-accent-text)", label: "Accent" },
284
+ { token: "var(--color-code-text)", label: "Code" }
285
+ ]
286
+
287
+ const EDITOR_BG_TOKENS = [
288
+ { token: "var(--surface-bg)", label: "Background" },
289
+ { token: "var(--color-highlight)", label: "Highlight" },
290
+ { token: "var(--color-brand)", label: "Brand" },
291
+ { token: "var(--color-accent-border)", label: "Accent" },
292
+ { token: "var(--color-danger)", label: "Danger" },
293
+ { token: "var(--color-warning)", label: "Warning" },
294
+ { token: "var(--color-code-bg)", label: "Code" },
295
+ { token: "var(--surface-hover)", label: "Hover" }
296
+ ]
297
+
298
+ function resolveColorForInput(color) {
299
+ if (!color || !color.startsWith("var(")) return color
300
+ const match = color.match(/^var\(([^)]+)\)$/)
301
+ if (!match) return color
302
+ return getComputedStyle(document.documentElement).getPropertyValue(match[1]).trim() || color
303
+ }
304
+
305
+ function ToolbarColorPicker({ icon, title, color, onChange, onClear, colorType }) {
277
306
  const [open, setOpen] = useState(false)
278
307
  const triggerRef = useRef(null)
279
308
  const popoverRef = useRef(null)
309
+ const tokens = colorType === "background" ? EDITOR_BG_TOKENS : EDITOR_TEXT_TOKENS
280
310
 
281
311
  useEffect(() => {
282
312
  if (!open) return
@@ -294,6 +324,8 @@ function ToolbarColorPicker({ icon, title, color, onChange, onClear }) {
294
324
  return () => document.removeEventListener("mousedown", handleClick)
295
325
  }, [open])
296
326
 
327
+ const resolvedColor = resolveColorForInput(color)
328
+
297
329
  return (
298
330
  <div className="lexical-toolbar-color" title={title}>
299
331
  <button
@@ -306,20 +338,37 @@ function ToolbarColorPicker({ icon, title, color, onChange, onClear }) {
306
338
  </button>
307
339
  {open ? (
308
340
  <div className="lexical-toolbar-color__popover" ref={popoverRef}>
309
- <input
310
- type="color"
311
- value={color}
312
- onChange={(event) => onChange(event.target.value)}
313
- />
314
- <button
315
- type="button"
316
- className="lexical-toolbar-btn lexical-toolbar-btn--small"
317
- onClick={() => {
318
- onClear()
319
- setOpen(false)
320
- }}>
321
-
322
- </button>
341
+ <div className="lexical-toolbar-color__tokens">
342
+ {tokens.map(({ token, label }) => (
343
+ <button
344
+ key={token}
345
+ type="button"
346
+ className={`lexical-toolbar-color__token-btn${color === token ? " active" : ""}`}
347
+ style={{ backgroundColor: token }}
348
+ title={label}
349
+ onClick={() => {
350
+ onChange(token)
351
+ setOpen(false)
352
+ }}
353
+ />
354
+ ))}
355
+ </div>
356
+ <div className="lexical-toolbar-color__custom-row">
357
+ <input
358
+ type="color"
359
+ value={resolvedColor.startsWith("#") ? resolvedColor : "#000000"}
360
+ onChange={(event) => onChange(event.target.value)}
361
+ />
362
+ <button
363
+ type="button"
364
+ className="lexical-toolbar-btn lexical-toolbar-btn--small"
365
+ onClick={() => {
366
+ onClear()
367
+ setOpen(false)
368
+ }}>
369
+
370
+ </button>
371
+ </div>
323
372
  </div>
324
373
  ) : null}
325
374
  </div>
@@ -696,6 +745,7 @@ function Toolbar() {
696
745
  icon="🎨"
697
746
  title="Text color"
698
747
  color={fontColor}
748
+ colorType="text"
699
749
  onChange={(value) => {
700
750
  setFontColor(value)
701
751
  applyTextStyle({ color: value })
@@ -709,6 +759,7 @@ function Toolbar() {
709
759
  icon="🖌️"
710
760
  title="Background color"
711
761
  color={bgColor}
762
+ colorType="background"
712
763
  onChange={(value) => {
713
764
  setBgColor(value)
714
765
  applyTextStyle({ "background-color": value })
@@ -83,6 +83,7 @@ class CreativeTreeRow extends LitElement {
83
83
  this._stopAnimation();
84
84
  }
85
85
  }
86
+
86
87
  }
87
88
 
88
89
  disconnectedCallback() {
@@ -75,8 +75,14 @@ function handleCreated(creative) {
75
75
  targetContainer.style.display = ''
76
76
  if (targetContainer.dataset) targetContainer.dataset.expanded = 'true'
77
77
 
78
- const newRow = createRow(creative)
79
- insertAtCorrectPosition(newRow, creative, targetContainer)
78
+ // Adjust broadcast level (absolute) to relative level for the current view
79
+ const relativeLevel = computeRelativeLevel(creative)
80
+ const adjustedCreative = relativeLevel != null
81
+ ? { ...creative, level: relativeLevel }
82
+ : creative
83
+
84
+ const newRow = createRow(adjustedCreative)
85
+ insertAtCorrectPosition(newRow, adjustedCreative, targetContainer)
80
86
  }
81
87
  // If no targetContainer found, the creative is relevant but we can't determine
82
88
  // the exact insertion point — it will appear on next page load.
@@ -230,6 +236,25 @@ function findRowsForCreative(creativeId, originId) {
230
236
  return rows
231
237
  }
232
238
 
239
+ // Compute the correct relative level for a creative based on its parent's DOM level.
240
+ // The broadcast sends absolute level (ancestors.size + 1), but the tree view uses
241
+ // relative levels starting at 1. Without this adjustment, broadcasts after drag & drop
242
+ // on sub-pages would overwrite the relative level with the absolute level, causing
243
+ // creatives to appear 1-3 levels deeper than expected.
244
+ function computeRelativeLevel(creative) {
245
+ const parentId = creative.parent_id
246
+ if (!parentId) return 1 // root-level creative
247
+
248
+ const parentRow = document.querySelector(`creative-tree-row[creative-id="${parentId}"]`)
249
+ if (!parentRow) return null // parent not in DOM — cannot compute
250
+
251
+ // Title row's children start at level 1 (same as title), not level + 1
252
+ if (parentRow.hasAttribute('is-title')) return 1
253
+
254
+ const parentLevel = Number(parentRow.getAttribute('level') || parentRow.level || 1)
255
+ return parentLevel + 1
256
+ }
257
+
233
258
  function handleUpdated(creative) {
234
259
  // Notify popup controllers about creative data changes (e.g. trigger state)
235
260
  // Must fire before the early return — popup may be open for a creative
@@ -241,6 +266,13 @@ function handleUpdated(creative) {
241
266
  const rows = findRowsForCreative(creative.id, creative.origin_id)
242
267
  if (rows.length === 0) return
243
268
 
269
+ // Adjust broadcast level (absolute) to relative level (based on parent's DOM level).
270
+ // This prevents the level from jumping when viewing sub-pages.
271
+ const relativeLevel = computeRelativeLevel(creative)
272
+ const adjustedCreative = relativeLevel != null
273
+ ? { ...creative, level: relativeLevel }
274
+ : creative
275
+
244
276
  // Find currently editing creative ID to skip it
245
277
  const editForm = document.querySelector('#inline-edit-form-element')
246
278
  const editingId = editForm?.dataset?.creativeId
@@ -248,11 +280,11 @@ function handleUpdated(creative) {
248
280
  rows.forEach(row => {
249
281
  if (String(creative.id) === String(editingId)) {
250
282
  if (creative.inline_editor_payload) {
251
- row.dataset.pendingSyncData = JSON.stringify(creative)
283
+ row.dataset.pendingSyncData = JSON.stringify(adjustedCreative)
252
284
  }
253
285
  return
254
286
  }
255
- applyRowProperties(row, creative)
287
+ applyRowProperties(row, adjustedCreative)
256
288
  })
257
289
  }
258
290
 
@@ -2,6 +2,9 @@
2
2
  <% if authenticated? %>
3
3
  <%= turbo_stream_from Current.user, :creative_tree %>
4
4
  <% end %>
5
+ <% if @auto_fullscreen %>
6
+ <% content_for :comments_popup_auto_fullscreen, "true" %>
7
+ <% end %>
5
8
 
6
9
  <div data-controller="creatives--import creatives--select-mode creatives--drag-drop creatives--expansion creatives--row-editor share-modal"
7
10
  data-creatives--import-parent-id-value="<%= @parent_creative&.id %>"
@@ -222,7 +225,6 @@
222
225
  }
223
226
  ) %>
224
227
 
225
- <%= render 'collavre/comments/comments_popup', auto_fullscreen: @auto_fullscreen %>
226
228
  <%= render 'inline_edit_form' %>
227
229
 
228
230
  <%= render_extension_slot(:creative_modals) %>
@@ -1,5 +1,10 @@
1
1
  <%= render_extension_slot(:navigation_panels) %>
2
2
 
3
+ <% if Current.user %>
4
+ <%= render 'collavre/comments/comments_popup',
5
+ auto_fullscreen: content_for?(:comments_popup_auto_fullscreen) %>
6
+ <% end %>
7
+
3
8
  <div id="creative-guide-popover" style="display:none; position:fixed; top:60px; left:50%; transform:translateX(-50%); background:white; border:1px solid var(--color-border); box-shadow:0 2px 8px rgba(0,0,0,0.12); padding:1.2em; max-width:400px; z-index:1000; border-radius:8px;">
4
9
  <button type="button" id="close-creative-guide" class="popup-close-btn">&times;</button>
5
10
  <div style="margin-top:0.5em; color:#444; font-size:1em; line-height:1.5;">
@@ -1,5 +1,5 @@
1
1
  <% has_comments = count.positive? || show_zero %>
2
- <% display = has_comments ? 'inline-block' : 'none' %>
2
+ <% display = has_comments ? 'inline-flex' : 'none' %>
3
3
  <span id="<%= badge_id %>" class="badge" style="display:<%= display %>" data-count="<%= count %>"
4
4
  data-controller="comment-badge" data-comment-badge-has-comments-value="<%= has_comments %>"
5
5
  ><%= count if has_comments %></span>
@@ -1,3 +1,3 @@
1
1
  module Collavre
2
- VERSION = "0.12.0"
2
+ VERSION = "0.12.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: collavre
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Collavre