Skip to content

Update fork design#147

Merged
taoeffect merged 127 commits intomasterfrom
update-fork-design
Mar 3, 2026
Merged

Update fork design#147
taoeffect merged 127 commits intomasterfrom
update-fork-design

Conversation

@pieer
Copy link
Collaborator

@pieer pieer commented Feb 23, 2026

This PR updates the pull request review UI, improves internationalization, and standardizes CSS practices across the fork design pages.

Changes
🎨 UI & Styling
Review modal: Replaced the tippy.js popup with a proper overlay modal for submitting pull request reviews
Renamed new_review.tmpl → review_submit.tmpl for clarity
Split diff headers: Added styled column headers ("Current article" / "Change requested") to the split diff view
CSS variables: Replaced ~60 hardcoded hex/rgb colors across 6 CSS files with semantic CSS variables (e.g., --color-text-primary, --color-border, --color-surface-muted)
Font weights: Replaced hardcoded font-weight values (500, 600, 700) with CSS variable equivalents (--font-weight-medium, --font-weight-semibold, --font-weight-bold)
🌐 Internationalization
Added 12 new locale keys for the review modal, split diff headers, and back navigation
Replaced all hardcoded English strings in review_submit.tmpl,
section_split.tmpl
, and
view_title.tmpl
🧹 Code Quality
Removed inline <script> from the review template — moved logic to
repo-issue.ts
using data attributes and class hooks
Removed all stylelint-disable comments (13 total) by converting values to CSS variables
Removed unused createTippy import from
repo-issue.ts
Fixed getElementById → querySelector per ESLint rules
Fixed javascript:history.back() → proper onclick handler in
view_title.tmpl
Added
switchToUserPreference()
no-op to
ToastCommentEditor
for API compatibility


Open with Devin

* set fork-related context data
* show submit button based on permissions
* add ForkAndEdit field to EditRepoFileForm
* modify EditFilePost to handle fork creation and commit to fork
* reuse existing fork if user already has one
* query repos by owner and subject
* check for existing user repo for subject
* disable edit/fork buttons when user already has a repo for the subject
* show message in edit view explaining why editing is disabled
* fix wrong error condition (returned on any error, not just 'not found')
* fix double increment bug (i++ in loop body + loop increment)
* optimise from O(n) queries to single query with hash set lookup O(1)
* add proper error logging
* run subject ownership check and fork detection concurrently
* add error for subject ownership violations
* validate subject ownership before forking
* fix POST handler to validate subject ownership
* create IDX_repository_owner_subject and IDX_repository_owner_fork
* optimizes GetRepositoryByOwnerIDAndSubjectID() and GetForkedRepo() queries
* replace 'Cannot Edit' button with 'Edit Your Article' action link
* add translation keys for fork-on-edit error messages
* link users to their existing article when subject ownership blocks editing
* simplify fork_and_edit hidden field to only use .NeedsFork
* extract repeated nil-guard pattern into $subjectName template variable
* add Playwright tests for modal appearance, cancel actions, and tooltip
* test modal shows correct header, content, and button text
* verify cancel button and Escape key close modal without action
* verify fork button has tooltip with confirmation message
* add repository owner tests for Submit Changes button behavior
* add non-owner tests for Fork button and disabled submit button
* add unauthenticated user test for sign-in button
* accessibility tests verify Enter opens modal, Tab navigates buttons, and buttons have proper accessible text content
* unique name tests verify suffix generation when base name is taken
* use RepoOperationsLink instead of RepoLink in form action url
* bypass fork_and_edit in CanWriteToBranch middleware to allow non-owners to reach the EditFilePost handler
* skip NeedFork workflow when ForkAndEdit is true to prevent conflicting fork creation attempts
* CheckForkOnEditPermissions for owner/non-owner/unauthenticated
* CanWriteToBranch middleware bypass with fork_and_edit=true
* existing fork detection using fixture data (repo11/repo10)
* subject ownership blocking prevents duplicate forks
* form action URL uses RepoOperationsLink not RepoLink
* slug generation migration covering basic slugs, mixed-case deduplication, special characters, unique constraint verification, and empty table handling
* index creation, idempotency, and index type verification
…order

* fork API behavior when user already owns a repository for the same subject
* fork blocked by subject ownership (403), fork succeeds for user without subject repo (202), fork blocked even with custom name (403)
* fix error handling order in CreateFork
* update GetForkedRepo signature to return (*Repository, error)
* update all callers to handle the new error return value
* fixes silent error swallowing
* fix modal promise resolution pattern
* add PathEscapeSegments to article template URL
* update test comment to list all removed special characters
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 potential issues.

View 7 additional findings in Devin Review.

Open in Devin Review

pieer added 2 commits March 3, 2026 00:58
…th check

- Make navbar-pill-active conditional on PageIsPullList so it's only
  highlighted on PR-related pages (fixes always-active pill bug)
- Add $.SignedUserID check to review submit button condition to prevent
  showing it to unauthenticated users
- Fix split/unified view toggle (replace broken JS onclick with server-side
  rendered <a> tag)
- Add mobile default to unified view
- Add CSS overrides for conversation-holder comments and PR diff styling
Remove .IsShowingAllCommits from the outer condition in box.tmpl since
review_submit.tmpl already handles the disabled state internally when
not showing all commits. This matches the master behavior where the
review button is always rendered for signed-in users on PR file pages.

Fixes TestPullDiff_SingleCommitPRDiff and TestPullDiff_CommitRangePRDiff
which expect .js-btn-review to exist with a disabled class.
@pieer
Copy link
Collaborator Author

pieer commented Mar 2, 2026

/crush_fast


AI review started.

@github-actions
Copy link

github-actions bot commented Mar 2, 2026

Advanced AI Review

  • Type: Agentic (crush)
  • Model: glm-5
Click to expand review

Based on my thorough review of these changes, here are the issues I've identified:


🔴 Issue 1: Missing data-fork-confirm-body2 attribute in template

  • Addressed
  • Dismissed

Location: custom/templates/shared/repo/article.tmpl:97-103

The JavaScript code expects a data-fork-confirm-body2 attribute:

// web_src/js/features/article-editor.ts:71
const body2 = forkArticleButton.getAttribute('data-fork-confirm-body2') || '';

But the template only provides data-fork-confirm-body:

<button type="button" id="fork-article-button" class="ui secondary button"
    data-fork-confirm-title="{{ctx.Locale.Tr "repo.fork_article_confirm_title"}}"
    data-fork-confirm-body="{{ctx.Locale.Tr "repo.fork_article_confirm_body"}}"
    data-fork-confirm-yes="{{ctx.Locale.Tr "repo.fork_article_confirm_yes"}}"
    data-fork-confirm-cancel="{{ctx.Locale.Tr "repo.fork_article_confirm_cancel"}}">

Impact: The second paragraph in the fork confirmation modal will always be empty.

Fix: Add the missing attribute to the template:

<button type="button" id="fork-article-button" class="ui secondary button"
    data-fork-confirm-title="{{ctx.Locale.Tr "repo.fork_article_confirm_title"}}"
    data-fork-confirm-body="{{ctx.Locale.Tr "repo.fork_article_confirm_body"}}"
    data-fork-confirm-body2="{{ctx.Locale.Tr "repo.fork_article_confirm_body2"}}"
    data-fork-confirm-yes="{{ctx.Locale.Tr "repo.fork_article_confirm_yes"}}"
    data-fork-confirm-cancel="{{ctx.Locale.Tr "repo.fork_article_confirm_cancel"}}">

🔴 Issue 2: Review title field is not persisted to backend

  • Addressed
  • Dismissed

Location: templates/repo/diff/review_submit.tmpl:29-32

The new review modal adds a title input field:

<div class="field">
    <label class="review-field-label">{{ctx.Locale.Tr "repo.diff.review.add_title"}}</label>
    <input type="text" name="title" class="review-title-input" placeholder="">
</div>

However, the backend SubmitReviewForm struct in services/forms/repo_form.go:580-585 only has:

type SubmitReviewForm struct {
    Content  string
    Type     string
    CommitID string
    Files    []string
}

Impact: The title field is silently ignored - users enter a title but it's never saved or used.

Fix options:

  1. Add Title field to SubmitReviewForm and update the review submission logic to use it
  2. Remove the title input from the modal if it's not intended to be stored

🔴 Issue 3: Large block of CSS commented out, potentially breaking non-PR pages

  • Addressed
  • Dismissed

Location: web_src/css/repo.css:30-84

A significant block of CSS has been commented out:

/*
.issue-content {
  display: flex;
  align-items: flex-start;
  gap: 16px;
}
...
.issue-content-right .ui.list {
  margin: 0.5em 0;
  max-width: 100%;
} */

However, the media query at line 96-100 still references these classes:

@media (max-width: 767.98px) {
  .issue-content-left,
  .issue-content-right {
    width: 100%;
  }
}

Impact: This will break the layout of issue pages (non-PR pages) since the base flexbox layout styles are removed but media queries remain.

Fix: Either:

  1. Remove the entire block including media queries if the intent is to fully replace it in repo.custom.css
  2. Restore the styles and only override what's needed for PRs using a more specific selector

🟡 Issue 4: Inline onclick handlers violate CSP best practices

  • Addressed
  • Dismissed

Locations:

  • templates/repo/diff/box.tmpl:7
  • templates/repo/issue/view_title.tmpl:14
<!-- box.tmpl:7 -->
<button class="info-dismiss" onclick="this.parentElement.style.display='none'; try{localStorage.setItem('diff_info_dismissed','1')}catch(e){}" aria-label="Dismiss">

<!-- view_title.tmpl:14 -->
<a href="#" onclick="history.back(); return false" class="pr-back-link">

Impact: Inline event handlers won't work if CSP restricts unsafe-inline in script-src. This creates inconsistent behavior and security concerns.

Fix: Move to event listeners in TypeScript:

// In repo-issue.ts or similar
document.querySelector('.info-dismiss')?.addEventListener('click', function() {
  this.parentElement.style.display = 'none';
  try { localStorage.setItem('diff_info_dismissed', '1'); } catch {}
});

document.querySelector('.pr-back-link')?.addEventListener('click', (e) => {
  e.preventDefault();
  history.back();
});

🟡 Issue 5: Missing Escape key handler for review modal (accessibility)

  • Addressed
  • Dismissed

Location: web_src/js/features/repo-issue.ts:322-340

The review modal implementation only handles click events but not keyboard navigation:

if (elReviewBtn && elReviewOverlay) {
  elReviewBtn.addEventListener('click', (e) => {
    e.preventDefault();
    e.stopPropagation();
    elReviewOverlay.style.display = 'flex';
  });
  elReviewClose?.addEventListener('click', (e) => {
    e.preventDefault();
    elReviewOverlay.style.display = 'none';
  });
  elReviewOverlay.addEventListener('click', (e) => {
    if (e.target === elReviewOverlay) {
      elReviewOverlay.style.display = 'none';
    }
  });
}

Impact: Users cannot close the modal with the Escape key, which is expected behavior and an accessibility concern.

Fix:

// Add Escape key handler
document.addEventListener('keydown', (e) => {
  if (e.key === 'Escape' && elReviewOverlay?.style.display === 'flex') {
    elReviewOverlay.style.display = 'none';
  }
});

🟡 Issue 6: Hardcoded English strings not internationalized

  • Addressed
  • Dismissed

Locations:

  • templates/repo/diff/comment_form.tmpl:39,41,43
  • templates/repo/diff/box.tmpl:6
<!-- comment_form.tmpl -->
<button name="single_review" value="true" type="submit" class="ui submit tiny basic button btn-add-single">Post immediately</button>
<button name="pending_review" type="submit" class="ui submit primary tiny button btn-add-comment">Save draft</button>
<button name="pending_review" type="submit" class="ui submit primary tiny button btn-start-review">Save draft</button>

<!-- box.tmpl -->
<span class="info-text">Click a paragraph to add a comment.</span>

Impact: Non-English users will see mixed language UI.

Fix: Add locale keys and use {{ctx.Locale.Tr "..."}}:

; custom/options/locale/locale_en-US.ini
[repo.diff.comment]
post_immediately = Post immediately
save_draft = Save draft
info_banner_text = Click a paragraph to add a comment.

🟡 Issue 7: Mobile redirect on every page load

  • Addressed
  • Dismissed

Location: templates/repo/diff/box.tmpl:51-59

<script>
    // Default to unified view on mobile if no explicit style param is set
    (function() {
        var params = new URLSearchParams(window.location.search);
        if (!params.has('style') && window.innerWidth <= 768) {
            params.set('style', 'unified');
            window.location.search = params.toString();
        }
    })();
</script>

Impact: This causes a page reload on every mobile visit when there's no style parameter. Combined with the changed default in middlewares.go (which defaults to split view), this creates inconsistent behavior - the server renders split, but JS redirects to unified.

Fix: Handle this consistently on the server side by passing the viewport width or using CSS media queries instead of JavaScript redirects. Alternatively, set a cookie/sessionStorage to prevent redirect loops.


⚪ Issue 8: formatBoldText function is fragile

  • Addressed
  • Dismissed

Location: web_src/js/features/article-editor.ts:7-10

// Convert markdown-style bold (**text**) to <strong> tags
function formatBoldText(text: string): string {
  // eslint-disable-next-line github/unescaped-html-literal
  return text.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
}

Impact: This manual markdown parsing is fragile:

  • Doesn't handle ** at word boundaries correctly
  • Could produce unexpected results with nested or malformed markdown
  • The regex (.+?) could match across unintended boundaries

Since the input comes from trusted locale strings, this isn't a security issue, but it's fragile code.

Fix: Consider using a proper markdown parser or simply allow the locale strings to contain HTML directly (since they're trusted) with appropriate documentation.


⚪ Issue 9: Dead code should be removed, not commented

  • Addressed
  • Dismissed

Location: web_src/css/repo.css:90-94

/* .issue-content-right .ui.list.labels-list {
  display: flex;
  gap: var(--gap-inline);
  flex-wrap: wrap;
} */

Impact: Clutters the codebase and can cause confusion about whether code is intentionally disabled or forgotten.

Fix: Remove commented code entirely. Use git history to recover if needed.


Summary

Priority Count
🔴 High 3
🟡 Medium 4
⚪ Low 2

The most critical issues are:

  1. Missing template attribute - The fork modal won't display correctly
  2. Unused title field - Misleading UX where user input is silently discarded
  3. Commented CSS - Likely breaks non-PR issue pages

Review generated using glm-5 via Z.AI. Comment /review to re-run.

pieer added 8 commits March 3, 2026 02:24
The JavaScript in article-editor.ts reads data-fork-confirm-body2 to
display a second paragraph in the fork confirmation modal, but the
template was missing this attribute. The locale key already exists.

Fixes AI Review Issue #1.
The backend SubmitReviewForm struct has no Title field, so the title
input was silently discarded. This removes it to avoid misleading users.

Fixes AI Review Issue #2.
Remove large blocks of commented-out CSS for .issue-content-* classes
and .labels-list, plus the orphaned media query that referenced deleted
classes. Git history preserves these styles if needed.

Fixes AI Review Issues #3 and #9.
Move inline onclick handlers to addEventListener calls for CSP
compatibility. Affects the diff info banner dismiss button in box.tmpl
and the PR back link in view_title.tmpl.

Fixes AI Review Issue #4.
The review modal could only be closed by clicking the X button or the
overlay. Add a keydown listener so pressing Escape also closes it.

Fixes AI Review Issue #5.
Replace 'Post immediately', 'Save draft', and 'Click a paragraph to
add a comment.' with ctx.Locale.Tr calls. Add corresponding keys to
the custom locale file.

Fixes AI Review Issue #6.
Add sessionStorage guard so the automatic redirect from split to
unified view on mobile only fires once per session. Previously, if
a user switched back to split view, they'd be immediately redirected
again.

Fixes AI Review Issue #7.
@pieer pieer marked this pull request as ready for review March 3, 2026 10:05
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 2 new potential issues.

View 12 additional findings in Devin Review.

Open in Devin Review

@pieer pieer requested a review from pedrogaudencio March 3, 2026 15:29
Copy link
Collaborator

@pedrogaudencio pedrogaudencio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've merged the two stacked PRs (/pull/152 and /pull/154) with the latest changes from update-fork-design and everything seems to be working well now. 👍

Seems good from my end!

@taoeffect taoeffect merged commit f21aefc into master Mar 3, 2026
33 checks passed
@taoeffect taoeffect deleted the update-fork-design branch March 3, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants