Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 69 additions & 65 deletions engine/app/assets/stylesheets/coplan/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
color-scheme: light;

/* Colors */
--color-bg: #fafafa;
/* Slightly cool and dark for a page background: white cards need
something to stand out against, or light mode reads as fog. */
--color-bg: #eef0f5;
--color-bg-muted: #f3f4f6;
--color-surface: #ffffff;
--color-surface-muted: #f9fafb;
Expand Down Expand Up @@ -108,7 +110,11 @@
--glass-edge: rgba(255, 255, 255, 0.75);
--glass-blur: blur(16px) saturate(1.6);
--shadow-glass: inset 0 1px 0 rgba(255, 255, 255, 0.7), 0 1px 2px rgba(15, 23, 42, 0.04), 0 10px 30px rgba(15, 23, 42, 0.08);
--surface-translucent: rgba(255, 255, 255, 0.6);
--surface-translucent: rgba(255, 255, 255, 0.75);
/* Card outline. The white --glass-edge reads as a specular highlight on
dark backdrops but disappears white-on-white — cards and rows use this
faintly dark line instead so light mode has real definition. */
--card-edge: rgba(24, 33, 54, 0.10);
}

@media (prefers-color-scheme: dark) {
Expand Down Expand Up @@ -184,6 +190,7 @@
--glass-blur: blur(16px) saturate(1.4);
--shadow-glass: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.4);
--surface-translucent: rgba(21, 32, 51, 0.55);
--card-edge: rgba(148, 163, 184, 0.16);
}
}

Expand Down Expand Up @@ -260,6 +267,7 @@
--glass-blur: blur(16px) saturate(1.4);
--shadow-glass: inset 0 1px 0 rgba(255, 255, 255, 0.06), 0 1px 2px rgba(0, 0, 0, 0.3), 0 12px 32px rgba(0, 0, 0, 0.4);
--surface-translucent: rgba(21, 32, 51, 0.55);
--card-edge: rgba(148, 163, 184, 0.16);
}

/* Forced light theme — overrides system dark preference */
Expand Down Expand Up @@ -669,7 +677,7 @@ img, svg {
background: var(--glass-bg-strong);
-webkit-backdrop-filter: var(--glass-blur);
backdrop-filter: var(--glass-blur);
border: 1px solid var(--glass-edge);
border: 1px solid var(--card-edge);
border-radius: var(--radius-lg);
padding: var(--space-lg);
box-shadow: var(--shadow-glass);
Expand Down Expand Up @@ -2093,6 +2101,12 @@ img.avatar {
width: 220px;
flex-shrink: 0;
max-height: calc(100vh - var(--nav-height) - var(--space-xl) * 2);
/* The nav is the scroll container — a long outline must not push the
back-matter links (References/Attachments) below the sticky bound
where no amount of page scrolling reaches them. */
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
transition: width 0.2s ease, opacity 0.2s ease;
margin-right: var(--space-lg);
}
Expand Down Expand Up @@ -2141,10 +2155,8 @@ img.avatar {
list-style: none;
padding: 0;
margin: 0;
overflow-y: auto;
max-height: calc(100vh - var(--nav-height) - 8rem);
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
/* No inner scroll region: the whole nav scrolls (see .content-nav), so
the outline and the back-matter links share one scroll. */
}

.content-nav__item {
Expand Down Expand Up @@ -2538,27 +2550,26 @@ img.avatar {
/* Plan document + footnotes: one page, no tabs. The document is the main
glass card; references and attachments follow like back matter — same
page, quieter surface. */
/* Back matter lives inside the content column, closing out the document
after a rule — no card-within-card, no space below the sidebar. Empty
sections are a single quiet line. */
.plan-footnotes {
margin-top: var(--space-xl);
margin-top: var(--space-2xl);
padding-top: var(--space-lg);
border-top: 1px solid var(--color-border);
display: flex;
flex-direction: column;
gap: var(--space-lg);
}

.plan-footnote {
/* Set apart from the document proper: flatter, edge-lit, no heavy card. */
background: var(--surface-translucent);
border: 1px solid var(--glass-edge);
border-left: 3px solid var(--color-border);
border-radius: var(--radius);
scroll-margin-top: var(--space-lg);
scroll-margin-top: calc(var(--nav-height) + var(--space-lg));
}

.plan-footnote__header {
display: flex;
align-items: center;
gap: var(--space-sm);
padding: var(--space-md) var(--space-lg) 0;
}

.plan-footnote__title {
Expand Down Expand Up @@ -2737,8 +2748,17 @@ img.avatar {
}

/* References section */
.references-section {
padding: var(--space-lg);
/* Section bodies hang under the header, indented past the round "+" so
content lines up with the section title. */
.references-section,
.attachments-section {
padding: 0 0 0 calc(24px + var(--space-sm));
}

.plan-footnote__empty {
margin: var(--space-xs) 0 0;
font-size: var(--text-sm);
color: var(--color-text-muted);
}

.references-list {
Expand Down Expand Up @@ -2942,10 +2962,6 @@ img.avatar {
}

/* Attachments section */
.attachments-section {
padding: var(--space-lg);
}

.attachments-section__header {
margin-bottom: var(--space-md);
}
Expand Down Expand Up @@ -4539,6 +4555,12 @@ img.avatar {
top: calc(var(--nav-height) + var(--space-md));
font-size: var(--text-sm);
min-width: 0;
/* A sticky sidebar taller than the viewport pins its overflow below the
fold forever — page scroll can't reach it. Scroll the sidebar itself. */
max-height: calc(100vh - var(--nav-height) - var(--space-md) * 2);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--color-border) transparent;
}

.workspace__sidebar-sections {
Expand Down Expand Up @@ -4900,17 +4922,18 @@ img.avatar {
history, quieter than the warning-toned attention strip. */
.recent-updates {
margin-bottom: var(--space-lg);
padding: var(--space-sm) var(--space-md);
border: 1px solid var(--glass-edge);
padding: var(--space-md);
border: 1px solid var(--card-edge);
border-radius: var(--radius);
background: var(--surface-translucent);
box-shadow: var(--shadow);
}

.recent-updates__heading {
display: flex;
align-items: center;
gap: var(--space-xs);
margin: 0 0 var(--space-xs);
margin: 0 0 var(--space-sm);
font-size: 0.8rem;
font-weight: 600;
text-transform: uppercase;
Expand All @@ -4924,7 +4947,8 @@ img.avatar {
padding: 0;
display: flex;
flex-direction: column;
gap: 2px;
/* Roomy enough to scan — this read as one bunched block at 2px. */
gap: var(--space-sm);
}

.recent-updates__item {
Expand Down Expand Up @@ -5014,9 +5038,10 @@ img.avatar {
gap: var(--space-sm);
padding: var(--space-sm) var(--space-md);
background: var(--surface-translucent);
border: 1px solid var(--glass-edge);
border: 1px solid var(--card-edge);
border-radius: var(--radius);
margin-bottom: var(--space-xs);
box-shadow: var(--shadow);
color: var(--color-text);
text-decoration: none;
transition: background 0.12s ease, box-shadow 0.12s ease;
Expand Down Expand Up @@ -5058,9 +5083,10 @@ img.avatar {
/* Translucent without backdrop blur — rows are numerous and per-row
blur is a GPU tax; the mesh still glows through faintly. */
background: var(--surface-translucent);
border: 1px solid var(--glass-edge);
border: 1px solid var(--card-edge);
border-radius: var(--radius);
margin-bottom: var(--space-xs);
box-shadow: var(--shadow);
transition: background 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
/* Selectable text hijacks press-and-drag: the browser starts a text
selection instead of the row drag, and the mouseup then *navigates*
Expand Down Expand Up @@ -5148,8 +5174,15 @@ img.avatar {
text-decoration: none;
}

.plan-row__spacer {
flex: 1;
/* Byline pinned to the card's top-right corner: it must not ride along
with wrapping titles or drift to a second line mid-card. */
.plan-row__meta {
display: flex;
align-items: center;
gap: var(--space-sm);
margin-left: auto;
align-self: flex-start;
flex-shrink: 0;
}

.plan-row__time {
Expand All @@ -5171,40 +5204,6 @@ img.avatar {
overflow: hidden;
}

/* Row bookmark — the keyboard/no-drag door into the folder navigator. */
.plan-row__save {
display: inline-flex;
align-items: center;
padding: 4px;
background: none;
border: none;
border-radius: var(--radius);
color: var(--color-text-muted);
cursor: pointer;
opacity: 0;
transition: opacity 0.12s ease, color 0.12s ease;
}

.plan-row:hover .plan-row__save,
.plan-row__save:focus-visible,
.plan-row__save--saved {
opacity: 1;
}

.plan-row__save:hover {
color: var(--color-primary);
background: var(--color-bg-muted);
}

.plan-row__save--saved {
color: var(--color-primary);
}

/* Saved = a second click removes; hint at that on hover. */
.plan-row__save--saved:hover {
color: var(--color-danger, #e0637a);
}

/* Toasts (drag & drop feedback) */
.toasts {
position: fixed;
Expand Down Expand Up @@ -5232,6 +5231,8 @@ img.avatar {
tap to expand, out of the way otherwise. */
.workspace__sidebar {
position: static;
max-height: none;
overflow: visible;
}

.workspace__sidebar-toggle {
Expand Down Expand Up @@ -5326,10 +5327,13 @@ img.avatar {
overflow-wrap: anywhere;
}

.plan-document,
.plan-document {
padding: var(--space-md);
}

.references-section,
.attachments-section {
padding: var(--space-md);
padding-left: 0;
}

.plan-actions {
Expand Down
5 changes: 2 additions & 3 deletions engine/app/views/coplan/plans/_attachments.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,7 @@
<% end %>
</ul>
<% else %>
<div class="empty-state">
<p>No attachments yet.<% if can_add %> Upload files to share diagrams, screenshots, or documents alongside the plan.<% end %></p>
</div>
<%# One quiet line, not a padded card — empty is the common case. %>
<p class="plan-footnote__empty">None yet<% if can_add %> — use the + to add screenshots, diagrams, or files<% end %>.</p>
<% end %>
</div>
32 changes: 32 additions & 0 deletions engine/app/views/coplan/plans/_footnotes.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<%# Back matter: references and attachments close out the document inside
the content column — separated from the prose by a rule, not banished
below the sidebar where long outlines made them collide. Each section
is one quiet header line (+, title, count); empty sections stay one
line tall. %>
<div class="plan-footnotes">
<section class="plan-footnote" id="footnote-references" data-plan-section="references" aria-labelledby="footnote-references-title">
<div class="plan-footnote__header">
<% if current_user && allowed_to?(plan, :contribute?) %>
<button type="button" class="section-add" popovertarget="add-reference-modal" title="Add a reference" aria-label="Add a reference">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</button>
<% end %>
<h2 class="plan-footnote__title" id="footnote-references-title">References</h2>
<span class="section-count" id="references-count"><%= references.size %></span>
</div>
<%= render partial: "coplan/plans/references", locals: { references: references, plan: plan } %>
</section>

<section class="plan-footnote" id="footnote-attachments" data-plan-section="attachments" aria-labelledby="footnote-attachments-title">
<div class="plan-footnote__header">
<% if current_user && allowed_to?(plan, :contribute?) %>
<button type="button" class="section-add" popovertarget="add-attachment-modal" title="Add files" aria-label="Add files">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
</button>
<% end %>
<h2 class="plan-footnote__title" id="footnote-attachments-title">Attachments</h2>
<span class="section-count" id="attachments-count"><%= attachments.size %></span>
</div>
<%= render partial: "coplan/plans/attachments", locals: { attachments: attachments, plan: plan } %>
</section>
</div>
29 changes: 6 additions & 23 deletions engine/app/views/coplan/plans/_plan_row.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,35 +37,18 @@
<%= folder_paths_by_id[my_folder_id] %>
<% end %>
<% end %>
<% plan.tags.each do |tag| %>
<%= link_to tag.name,
workspace_path(tag: tag.name),
class: "badge badge--tag #{'badge--tag-active' if params[:tag] == tag.name}",
draggable: false,
data: { turbo_frame: "_top" } %>
<% end %>
<span class="plan-row__spacer"></span>
<% if unread > 0 %>
<span class="inbox-badge" title="<%= pluralize(unread, "unread comment") %>"><%= unread %></span>
<% end %>
<span class="plan-row__time text-muted" title="<%= plan.updated_at %>"><%= time_ago_in_words(plan.updated_at) %> ago</span>
<span class="plan-row__avatar"><%= link_to user_avatar(plan.created_by_user), profile_path_for(plan.created_by_user), title: plan.created_by_user.name, draggable: false, data: { turbo_frame: "_top" } %></span>
<%# Bookmark = the keyboard/no-drag path into the folder navigator.
Saved rows unsave on the second click, quietly. %>
<button type="button"
class="plan-row__save<%= " plan-row__save--saved" if my_folder_id %>"
data-action="coplan--folder-picker#open"
data-move-url="<%= move_to_folder_plan_path(plan) %>"
data-current-folder-id="<%= my_folder_id %>"
data-saved="<%= my_folder_id.present? %>"
data-plan-title="<%= plan.title %>"
title="<%= my_folder_id ? "Saved — click to remove from your library" : "Save to a folder in your library" %>"
aria-label="<%= my_folder_id ? "Remove “#{plan.title}” from your library" : "Save “#{plan.title}” to your library" %>">
<svg width="13" height="13" viewBox="0 0 24 24" fill="<%= my_folder_id ? "currentColor" : "none" %>" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z"/></svg>
</button>
</div>
<% if summary.present? %>
<p class="plan-row__summary text-muted"><%= summary %></p>
<% end %>
</div>
<%# Byline, anchored to the card's top-right corner regardless of how the
title wraps — a floating "author · when" would read as misalignment. %>
<div class="plan-row__meta">
<span class="plan-row__time text-muted" title="<%= plan.updated_at %>"><%= time_ago_in_words(plan.updated_at) %> ago</span>
<span class="plan-row__avatar"><%= link_to user_avatar(plan.created_by_user), profile_path_for(plan.created_by_user), title: plan.created_by_user.name, draggable: false, data: { turbo_frame: "_top" } %></span>
</div>
</article>
5 changes: 2 additions & 3 deletions engine/app/views/coplan/plans/_references.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,7 @@
<% end %>
</ul>
<% else %>
<div class="empty-state">
<p>No references yet. Links in the plan content are auto-extracted, or add one manually.</p>
</div>
<%# One quiet line, not a padded card — empty is the common case. %>
<p class="plan-footnote__empty">None yet — links in the document are picked up automatically.</p>
<% end %>
</div>
Loading