Files
anonymous_github/public/partials/anonymize.htm
T

442 lines
33 KiB
HTML

<div class="anonymize-page h-100">
<div class="container py-3" v-if="githubConnections?.appEnabled">
<span v-if="isUpdate">Connection: {{ githubConnection === 'github-app' ? 'GitHub App (read-only)' : 'Legacy OAuth' }}. <a href="/connections">Change connection</a></span>
<span v-if="!isUpdate">Repository access:
<button class="btn" :disabled="githubConnection === 'github-app'" @click="chooseGitHubConnection('github-app')">Read-only GitHub App</button>
<button v-if="githubConnections?.oauthConnected" class="btn" :disabled="githubConnection === 'oauth'" @click="chooseGitHubConnection('oauth')">Use existing OAuth access</button>
</span>
<button class="btn btn-ink" @click="grantGitHubAccess()">{{ githubConnections?.appConnected ? 'Allow repository access on GitHub' : 'Connect read-only GitHub access' }}</button>
<p class="mb-0"><small>Return here after confirming access on GitHub. Anonymization publishes the configured content, including content from private repositories. Gists currently use OAuth.</small></p>
</div>
<!-- ===== STATE 1: No URL — centered input ===== -->
<div class="anonymize-landing" v-show="!(sourceUrl)">
<div class="anonymize-landing-inner">
<div class="paper-crumbs">My work &nbsp;/&nbsp; <span class="here">New anonymization</span></div>
<h1 class="paper-page-title">New <em>anonymization</em></h1>
<p class="paper-page-lede">
Paste a GitHub repository, pull-request, or gist URL. We&rsquo;ll fetch
it, strip every trace of identity, and hand you back a stable link.
</p>
<div class="form-group mt-4 mb-2">
<label class="paper-field-label" for="sourceUrl-landing">Source URL</label>
<input id="sourceUrl-landing" type="text" class="form-control form-control-lg" placeholder="https://github.com/owner/repository" v-field="{ state: viewState, set: value =&gt; { sourceUrl = value }, value: sourceUrl, form: null, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: &#x27;default blur click&#x27; }, change: () =&gt; { urlSelected() } }">
</div>
<small class="form-text" style="color: var(--ink-muted);">
Repository, pull request (…/pull/42) and gist (gist.github.com/…) URLs are all accepted.
</small>
</div>
</div>
<!-- ===== STATE 2: URL provided — form (left) + preview (right) ===== -->
<div class="anonymize-workspace" v-show="sourceUrl">
<header class="anonymize-topbar">
<div class="anonymize-topbar-inner">
<div class="paper-crumbs">
<a href="/dashboard">My work</a> &nbsp;/&nbsp;
<span class="here">{{ isUpdate ? 'Edit anonymization' : 'New anonymization' }}</span>
</div>
<div class="anonymize-topbar-head">
<h1 class="paper-page-title anonymize-topbar-title">
<span v-if="!isUpdate">New <em>anonymization</em></span>
<span v-if="isUpdate">Edit <em>anonymization</em></span>
</h1>
<span class="type-badge" v-show="detectedType" :class="{&#x27;type-repo&#x27;: detectedType === &#x27;repo&#x27;, &#x27;type-pr&#x27;: detectedType === &#x27;pr&#x27;, &#x27;type-gist&#x27;: detectedType === &#x27;gist&#x27;}">
{{ detectedType === 'repo' ? 'Repo' : detectedType === 'pr' ? 'PR' : 'Gist' }}
</span>
</div>
</div>
</header>
<div class="anonymize-split">
<!-- Form column (left) -->
<div class="anonymize-form-col overflow-auto">
<form class="form needs-validation paper-settings-main" name="anonymize" novalidate v-form="viewState">
<section class="paper-settings-section">
<div class="paper-section-eyebrow">Source</div>
<div class="form-group">
<label class="paper-field-label" for="sourceUrl">GitHub URL</label>
<input type="text" class="form-control" name="sourceUrl" id="sourceUrl" :disabled="isUpdate &amp;&amp; detectedType !== &#x27;repo&#x27;" placeholder="Paste a GitHub repo or pull request URL" v-field="{ state: viewState, set: value =&gt; { sourceUrl = value }, value: sourceUrl, form: &quot;anonymize&quot;, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: &#x27;default blur click&#x27; }, change: () =&gt; { urlSelected() } }" :class="{&#x27;is-invalid&#x27;: anonymize?.sourceUrl?.invalid}">
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.github">
Please provide a valid GitHub URL.
</div>
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.access">
Not accessible. The organization may restrict access.
</div>
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.missing">
Does not exist or is not accessible.
</div>
<div class="invalid-feedback" v-show="anonymize?.sourceUrl?.errors?.used">
Already anonymized.
</div>
</div>
<div class="form-grid-2" v-if="detectedType === &#x27;repo&#x27;">
<div class="form-group">
<label class="paper-field-label" for="branch">Branch</label>
<div class="input-group">
<select class="form-control" id="branch" name="branch" v-field="{ state: viewState, set: value =&gt; { source.branch = value }, value: source?.branch, form: &quot;anonymize&quot;, options: {} }">
<option v-for="(b, index) in branches" v-text="b?.name" :value="(b?.name)"></option>
</select>
<div class="input-group-append">
<button class="btn" type="button" title="Refresh" data-toggle="tooltip" data-placement="bottom" @click="getBranches(true)">
<i class="fas fa-sync" aria-hidden="true"></i>
</button>
</div>
</div>
</div>
<div class="form-group">
<label class="paper-field-label" for="commit">Commit</label>
<input class="form-control" id="commit" :disabled="options.update" name="commit" pattern="[a-fA-Z0-9]{6,}" required v-field="{ state: viewState, set: value =&gt; { source.commit = value }, value: source?.commit, form: &quot;anonymize&quot;, options: {} }" :class="{&#x27;is-invalid&#x27;: anonymize?.commit?.invalid}">
<div class="invalid-feedback" v-show="anonymize?.commit?.errors?.pattern || anonymize?.commit?.errors?.required">
The commit SHA is not valid.
</div>
<div class="invalid-feedback" v-show="anonymize?.commit?.errors?.exists">
This commit no longer exists in the repository. Click refresh to get the latest.
</div>
</div>
</div>
<div class="form-check" v-show="detectedType">
<input class="form-check-input" type="checkbox" id="update" name="update" v-field="{ state: viewState, set: value =&gt; { options.update = value }, value: options?.update, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="update">Auto-update from GitHub</label>
<small class="form-text text-muted">Follow the branch and pull the latest commit automatically, at most once an hour.</small>
</div>
</section>
<section class="paper-settings-section" v-show="detectedType">
<div class="paper-section-eyebrow">Identity</div>
<div class="form-group" v-show="detectedType === &#x27;repo&#x27;">
<label class="paper-field-label" for="repoId">Anonymized repository ID</label>
<input type="text" class="form-control" name="repoId" id="repoId" :disabled="isUpdate" v-field="{ state: viewState, set: value =&gt; { repoId = value }, value: repoId, form: &quot;anonymize&quot;, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: &#x27;default blur click&#x27; } }" :class="{&#x27;is-invalid&#x27;: anonymize?.repoId?.invalid}">
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/r/{{ repoId }}</code>.</small>
<div class="invalid-feedback" v-show="anonymize?.repoId?.errors?.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" v-show="anonymize?.repoId?.errors?.used">{{ repoId }} is already used.</div>
</div>
<div class="form-group" v-show="detectedType === &#x27;pr&#x27;">
<label class="paper-field-label" for="pullRequestId">Anonymized pull request ID</label>
<input type="text" class="form-control" name="pullRequestId" id="pullRequestId" :disabled="isUpdate" v-field="{ state: viewState, set: value =&gt; { pullRequestId = value }, value: pullRequestId, form: &quot;anonymize&quot;, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: &#x27;default blur click&#x27; } }" :class="{&#x27;is-invalid&#x27;: anonymize?.pullRequestId?.invalid}">
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/pr/{{ pullRequestId }}</code>.</small>
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" v-show="anonymize?.pullRequestId?.errors?.used">{{ pullRequestId }} is already used.</div>
</div>
<div class="form-group" v-show="detectedType === &#x27;gist&#x27;">
<label class="paper-field-label" for="gistId">Anonymized gist ID</label>
<input type="text" class="form-control" name="gistId" id="gistId" :disabled="isUpdate" v-field="{ state: viewState, set: value =&gt; { gistId = value }, value: gistId, form: &quot;anonymize&quot;, options: { debounce: {default: 1000, blur: 0, click: 0}, updateOn: &#x27;default blur click&#x27; } }" :class="{&#x27;is-invalid&#x27;: anonymize?.gistId?.invalid}">
<small class="form-text text-muted">Your share link will be <code>anonymous.4open.science/gist/{{ gistId }}</code>.</small>
<div class="invalid-feedback" v-show="anonymize?.gistId?.errors?.format">ID can only contain letters and numbers.</div>
<div class="invalid-feedback" v-show="anonymize?.gistId?.errors?.used">{{ gistId }} is already used.</div>
</div>
<div class="form-group">
<label class="paper-field-label" for="conference">Conference <span class="paper-optional">(optional)</span></label>
<input class="form-control" id="conference" name="conference" v-field="{ state: viewState, set: value =&gt; { conference = value }, value: conference, form: &quot;anonymize&quot;, options: { debounce: { default: 800, blur: 0 }, updateOn: &#x27;default blur&#x27; } }" :class="{&#x27;is-invalid&#x27;: anonymize?.conference?.invalid}">
<small class="form-text text-muted" v-show="conference_data">
<a target="_blank" :href="safeUrl((conference_data?.url))">{{ conference_data?.name }}</a> expires {{ fmt?.date(conference_data?.endDate) }}.
</small>
<div class="invalid-feedback" v-show="anonymize?.conference?.errors?.activated">The conference is not activated.</div>
<small class="form-text text-muted" v-show="!conference_data">Link to a conference to apply its shared defaults.</small>
</div>
</section>
<section class="paper-settings-section" v-show="detectedType">
<div class="paper-section-eyebrow">Anonymization</div>
<div class="form-group">
<label class="paper-field-label" for="terms">Terms to redact</label>
<textarea class="form-control" id="terms" name="terms" rows="4" v-field="{ state: viewState, set: value =&gt; { terms = value }, value: terms, form: &quot;anonymize&quot;, options: { debounce: 250 } }" :class="{&#x27;is-invalid&#x27;: anonymize?.terms?.invalid}"></textarea>
<small class="form-text text-muted">One term per line (regex allowed). Replaced by <code>{{ site_options?.ANONYMIZATION_MASK }}-[N]</code>, or use <code>term=&gt;replacement</code> to pick your own (e.g. <code>Anonymous=&gt;ABC</code>).</small>
<div class="warning-feedback" v-show="termsRegexWarning">Regex characters detected. Escape them if unintentional.</div>
<div class="invalid-feedback" v-show="anonymize?.terms?.errors?.format">Terms are in an invalid format.</div>
</div>
</section>
<section class="paper-settings-section" v-show="detectedType">
<div class="paper-section-eyebrow">Display</div>
<div v-show="detectedType === &#x27;repo&#x27;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="link" name="link" v-field="{ state: viewState, set: value =&gt; { options.link = value }, value: options?.link, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="link">Keep links</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="image" name="image" v-field="{ state: viewState, set: value =&gt; { options.image = value }, value: options?.image, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="image">Display images</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="pdf" name="pdf" v-field="{ state: viewState, set: value =&gt; { options.pdf = value }, value: options?.pdf, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="pdf">Display PDFs</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="notebook" name="notebook" v-field="{ state: viewState, set: value =&gt; { options.notebook = value }, value: options?.notebook, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="notebook">Display Notebooks</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="page" name="page" v-field="{ state: viewState, set: value =&gt; { options.page = value }, value: options?.page, form: &quot;anonymize&quot;, options: {} }" :disabled="!details?.hasPage || (details?.pageSource &amp;&amp; details?.pageSource?.branch !== source?.branch)">
<label class="form-check-label" for="page">GitHub Pages</label>
<small class="form-text text-muted d-block" v-show="!details?.hasPage">
{{ fmt?.translate('WARNINGS.page_not_enabled_on_repo') }}
</small>
<small class="form-text text-muted d-block" v-show="details?.hasPage &amp;&amp; details?.pageSource &amp;&amp; details?.pageSource?.branch !== source?.branch">
{{ fmt?.translate('WARNINGS.page_branch_mismatch', { pageBranch: details?.pageSource?.branch, selectedBranch: source?.branch }) }}
</small>
</div>
</div>
<div v-show="detectedType === &#x27;gist&#x27;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="title-gist" name="title-gist" v-field="{ state: viewState, set: value =&gt; { options.title = value }, value: options?.title, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="title-gist">Gist description</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="content-gist" name="content-gist" v-field="{ state: viewState, set: value =&gt; { options.content = value }, value: options?.content, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="content-gist">File contents</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="comments-gist" name="comments-gist" v-field="{ state: viewState, set: value =&gt; { options.comments = value }, value: options?.comments, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="comments-gist">Comments</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="username-gist" name="username-gist" v-field="{ state: viewState, set: value =&gt; { options.username = value }, value: options?.username, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="username-gist">Usernames</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="date-gist" name="date-gist" v-field="{ state: viewState, set: value =&gt; { options.date = value }, value: options?.date, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="date-gist">Dates</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="origin-gist" name="origin-gist" v-field="{ state: viewState, set: value =&gt; { options.origin = value }, value: options?.origin, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="origin-gist">Source gist ID</label>
</div>
</div>
<div v-show="detectedType === &#x27;pr&#x27;">
<div class="form-check">
<input class="form-check-input" type="checkbox" id="title" name="title" v-field="{ state: viewState, set: value =&gt; { options.title = value }, value: options?.title, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="title">PR title</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="body" name="body" v-field="{ state: viewState, set: value =&gt; { options.body = value }, value: options?.body, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="body">PR body</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="diff" name="diff" v-field="{ state: viewState, set: value =&gt; { options.diff = value }, value: options?.diff, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="diff">Diff</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="comments" name="comments" v-field="{ state: viewState, set: value =&gt; { options.comments = value }, value: options?.comments, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="comments">Comments</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="username" name="username" v-field="{ state: viewState, set: value =&gt; { options.username = value }, value: options?.username, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="username">Usernames</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="date" name="date" v-field="{ state: viewState, set: value =&gt; { options.date = value }, value: options?.date, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="date">Dates</label>
</div>
<div class="form-check">
<input class="form-check-input" type="checkbox" id="origin" name="origin" v-field="{ state: viewState, set: value =&gt; { options.origin = value }, value: options?.origin, form: &quot;anonymize&quot;, options: {} }">
<label class="form-check-label" for="origin">Project name</label>
</div>
</div>
</section>
<section class="paper-settings-section" v-show="detectedType">
<div class="paper-section-eyebrow">Expiration</div>
<div class="form-grid-2">
<div class="form-group">
<label class="paper-field-label" for="expiration">Strategy</label>
<select class="form-control" id="expiration" name="expiration" v-field="{ state: viewState, set: value =&gt; { options.expirationMode = value }, value: options?.expirationMode, form: &quot;anonymize&quot;, options: {} }">
<option value="redirect">Redirect to GitHub when expired</option>
<option value="remove" selected>Remove when expired</option>
</select>
</div>
<div class="form-group">
<label class="paper-field-label" for="expirationDate">Expiration date</label>
<input class="form-control" type="date" name="expirationDate" id="expirationDate" required v-field="{ state: viewState, set: value =&gt; { options.expirationDate = value }, value: options?.expirationDate, form: &quot;anonymize&quot;, options: {} }" :class="{&#x27;is-invalid&#x27;: anonymize?.expirationDate?.invalid}" :min="(minExpirationDate)" :max="(maxExpirationDate)">
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.min">Pick a date in the future.</div>
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.max">Pick a date on or before {{ fmt?.date(maxExpirationDate) }}.</div>
<div class="invalid-feedback" v-show="anonymize?.expirationDate?.errors?.required || anonymize?.expirationDate?.errors?.date">Enter a valid expiration date.</div>
</div>
</div>
<small class="form-text text-muted" v-show="options?.expirationMode==&#x27;remove&#x27; &amp;&amp; options?.expirationDate">After {{ fmt?.date(options?.expirationDate) }}, the content will be removed.</small>
<small class="form-text text-muted" v-show="options?.expirationMode==&#x27;redirect&#x27; &amp;&amp; options?.expirationDate">After {{ fmt?.date(options?.expirationDate) }}, visitors will be redirected to GitHub.</small>
</section>
<section class="paper-settings-section" v-show="isUpdate &amp;&amp; detectedType === &#x27;repo&#x27;">
<div class="paper-section-eyebrow">Co-authors</div>
<p class="form-text text-muted" style="margin-bottom: 8px;">
Co-authors can view and edit these settings. They cannot delete the anonymization or manage co-authors.
</p>
<div class="form-group" v-show="role === &#x27;owner&#x27; || role === &#x27;admin&#x27;">
<label class="paper-field-label" for="coauthorSearch">Add a GitHub user</label>
<div style="position: relative;">
<input type="text" id="coauthorSearch" class="form-control" placeholder="Search GitHub username…" autocomplete="off" v-field="{ state: viewState, set: value =&gt; { coauthorSearch = value }, value: coauthorSearch, form: &quot;anonymize&quot;, options: { debounce: 300 }, change: () =&gt; { searchCoauthors() } }">
<div class="dropdown-menu show" style="display: block; max-height: 220px; overflow-y: auto; width: 100%;" v-show="coauthorResults?.length &gt; 0">
<a href="#" class="dropdown-item d-flex align-items-center" v-for="(u, index) in coauthorResults" @click.prevent="addCoauthor(u, $event)">
<img alt="" style="width: 22px; height: 22px; border-radius: 50%; margin-right: 8px;" :src="safeUrl((u?.photo))">
<span v-text="u?.username"></span>
</a>
</div>
</div>
<small class="form-text text-muted" v-show="coauthorError" v-text="coauthorError"></small>
</div>
<div class="coauthor-list">
<div class="coauthor-row d-flex align-items-center" style="padding: 6px 0; gap: 8px;" v-for="(c, index) in coauthors">
<img alt="" style="width: 24px; height: 24px; border-radius: 50%;" v-if="c?.photo" :src="safeUrl((c?.photo))">
<a target="_blank" v-text="c?.username" :href="safeUrl(&quot;https://github.com/&quot; + (c?.username))"></a>
<span class="type-badge type-coauthor">Co-author</span>
<button type="button" class="btn btn-sm" style="margin-left: auto;" title="Remove co-author" v-show="role === &#x27;owner&#x27; || role === &#x27;admin&#x27;" @click="removeCoauthor(c)">
<i class="fas fa-times"></i>
</button>
</div>
<div class="form-text text-muted" v-show="!coauthors || coauthors?.length === 0">
No co-authors yet.
</div>
</div>
</section>
<div class="alert alert-danger" role="alert" v-if="error" v-text="error"></div>
<div class="anonymize-submit-bar" v-show="detectedType">
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;repo&#x27; &amp;&amp; !isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizeRepo($event) })">
<i class="fas fa-user-secret mr-1"></i> Anonymize Repository
</button>
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;repo&#x27; &amp;&amp; isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizeRepo($event) })">
<i class="fas fa-save mr-1"></i> Update Repository
</button>
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;pr&#x27; &amp;&amp; !isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizePullRequest($event) })">
<i class="fas fa-user-secret mr-1"></i> Anonymize Pull Request
</button>
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;pr&#x27; &amp;&amp; isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizePullRequest($event) })">
<i class="fas fa-save mr-1"></i> Update Pull Request
</button>
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;gist&#x27; &amp;&amp; !isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizeGist($event) })">
<i class="fas fa-user-secret mr-1"></i> Anonymize Gist
</button>
<button type="submit" class="btn btn-ink" v-if="detectedType === &#x27;gist&#x27; &amp;&amp; isUpdate" @click.prevent="submitForm($event, () =&gt; { anonymizeGist($event) })">
<i class="fas fa-save mr-1"></i> Update Gist
</button>
</div>
</form>
</div>
<!-- Preview column (right) -->
<div class="anonymize-preview-col" v-if="detectedType === &#x27;repo&#x27; &amp;&amp; html_readme">
<div class="anonymize-preview-head">
<span class="paper-eyebrow">Live preview</span>
<span class="anonymize-preview-sub">README with redactions applied</span>
</div>
<div class="anonymize-preview-body markdown-body body" v-html="sanitize(html_readme)"></div>
</div>
<div class="anonymize-preview-col" v-if="detectedType === &#x27;gist&#x27; &amp;&amp; details">
<div class="anonymize-preview-head">
<span class="paper-eyebrow">Live preview</span>
<span class="anonymize-preview-sub">Gist with redactions applied</span>
</div>
<div class="anonymize-preview-body">
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
<h2 class="pr-title mb-1">
<span v-if="options?.title">{{ anonymizeGistContent(details?.gist?.description) || 'Untitled gist' }}</span>
<span class="badge" :class="{&#x27;badge-success&#x27;: details?.gist?.isPublic, &#x27;badge-secondary&#x27;: !details?.gist?.isPublic}">
{{ details?.gist?.isPublic ? 'public' : 'secret' }}
</span>
</h2>
<small v-if="options?.date" v-text="fmt?.date(details?.gist?.updatedDate)"></small>
</div>
<small v-if="options?.origin">Gist ID: {{ details?.source?.gistId }}</small>
<small v-if="options?.username &amp;&amp; details?.gist?.ownerLogin">By @{{ anonymizeGistContent(details?.gist?.ownerLogin) }}</small>
<div v-if="options?.content &amp;&amp; previewGistFiles?.length">
<ul class="pr-comments mt-3">
<li class="pr-comment" v-for="(file, index) in previewGistFiles">
<div class="pr-comment-head">
<strong v-text="file?.filename"></strong>
<span class="pr-comment-date" v-if="file?.language">{{ file?.language }}</span>
</div>
<gist-file :file="file" :terms="terms" :options="options"></gist-file>
</li>
</ul>
</div>
<div v-if="options?.comments &amp;&amp; details?.gist?.comments &amp;&amp; details?.gist?.comments?.length">
<h3 class="paper-section-eyebrow mt-3">Comments</h3>
<ul class="pr-comments">
<li class="pr-comment" v-for="(comment, index) in details?.gist?.comments">
<div class="pr-comment-head">
<span class="pr-comment-author" v-if="options?.username">
<i class="far fa-user"></i> @<span v-text="anonymizeGistContent(comment?.author)"></span>
</span>
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
</div>
<div class="pr-comment-body" v-if="options?.body">
<markdown :content="anonymizeGistContent(comment?.body)" :options="options" :terms="terms"></markdown>
</div>
</li>
</ul>
</div>
</div>
</div>
<div class="anonymize-preview-col" v-if="detectedType === &#x27;pr&#x27; &amp;&amp; details">
<div class="anonymize-preview-head">
<span class="paper-eyebrow">Live preview</span>
<span class="anonymize-preview-sub">Pull request with redactions applied</span>
</div>
<div class="anonymize-preview-body">
<div class="d-flex w-100 justify-content-between align-items-center flex-wrap">
<h2 class="pr-title mb-1">
<span v-if="options?.title">{{ anonymizePrContent(details?.pullRequest?.title) }}</span>
<span class="badge" :class="{&#x27;badge-success&#x27;:details?.pullRequest?.merged, &#x27;badge-warning&#x27;:details?.pullRequest?.state==&#x27;open&#x27;, &#x27;badge-danger&#x27;:details?.pullRequest?.state==&#x27;closed&#x27; &amp;&amp; !details?.pullRequest?.merged}">
{{ fmt?.title(details?.pullRequest?.merged ? "merged" : details?.pullRequest?.state) }}
</span>
</h2>
<small v-if="options?.date" v-text="fmt?.date(details?.pullRequest?.updatedDate)"></small>
</div>
<small v-if="options?.origin">Pull Request on {{ details?.pullRequest?.baseRepositoryFullName }}</small>
<div class="pr-body shadow-sm p-3 mb-4 rounded" style="background: var(--paper-bg-alt)" v-if="options?.body">
<markdown :content="anonymizePrContent(details?.pullRequest?.body)" :options="options" :terms="terms"></markdown>
</div>
<nav class="paper-tabs" role="tablist" v-if="options?.diff || options?.comments">
<button class="paper-tab" type="button" role="tab" v-if="options?.diff" :class="{&#x27;active&#x27;: prTabState?.active == &#x27;diff&#x27;}" @click="prTabState.active = &#x27;diff&#x27;">
<i class="fas fa-code"></i> Diff
</button>
<button class="paper-tab" type="button" role="tab" v-if="options?.comments" :class="{&#x27;active&#x27;: prTabState?.active == &#x27;comments&#x27;}" @click="prTabState.active = &#x27;comments&#x27;">
<i class="far fa-comment-dots"></i>
<span v-text="fmt.plural(details?.pullRequest?.comments?.length, {&#x27;0&#x27;: &#x27;No comments&#x27;, &#x27;one&#x27;: &#x27;1 comment&#x27;, &#x27;other&#x27;: &#x27;{} comments&#x27;})"></span>
</button>
</nav>
<div class="paper-tab-content">
<div v-if="options?.diff &amp;&amp; prTabState?.active == &#x27;diff&#x27;">
<div class="pr-diff" v-html="sanitize(fmt?.diff(anonymizePrContent(details?.pullRequest?.diff)))"></div>
</div>
<div v-if="options?.comments &amp;&amp; prTabState?.active == &#x27;comments&#x27;">
<ul class="pr-comments">
<li class="pr-comment" v-for="(comment, index) in details?.pullRequest?.comments">
<div class="pr-comment-head">
<span class="pr-comment-author" v-if="options?.username">
<i class="far fa-user"></i> @<span v-text="anonymizePrContent(comment?.author)"></span>
</span>
<span class="pr-comment-date" v-if="options?.date" v-text="fmt?.date(comment?.updatedDate)"></span>
</div>
<div class="pr-comment-body" v-if="options?.body">
<markdown :content="anonymizePrContent(comment?.body)" :options="options" :terms="terms"></markdown>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>