diff --git a/modules/git/blob.go b/modules/git/blob.go index 4eef5f0e2a..e2dc624e86 100644 --- a/modules/git/blob.go +++ b/modules/git/blob.go @@ -157,22 +157,6 @@ func (b *Blob) NewTruncatedReader(limit int64) (rc io.ReadCloser, fullSize int64 }, fullSize, nil } -// GetBlobContent Gets the truncated content of the blob as raw text -func (b *Blob) GetBlobContent(limit int64) (string, error) { - if limit <= 0 { - return "", nil - } - rc, fullSize, err := b.NewTruncatedReader(limit) - if err != nil { - return "", err - } - defer rc.Close() - - buf := make([]byte, min(fullSize, limit)) - _, err = io.ReadFull(rc, buf) - return string(buf), err -} - type BlobTooLargeError struct { Size, Limit int64 } diff --git a/modules/git/blob_test.go b/modules/git/blob_test.go index a4b8033941..7caa2d2de3 100644 --- a/modules/git/blob_test.go +++ b/modules/git/blob_test.go @@ -45,24 +45,6 @@ func TestBlob(t *testing.T) { testBlob, err := repo.GetBlob("6c493ff740f9380390d5c9ddef4af18697ac9375") require.NoError(t, err) - t.Run("GetBlobContent", func(t *testing.T) { - r, err := testBlob.GetBlobContent(100) - require.NoError(t, err) - require.Equal(t, "file2\n", r) - - r, err = testBlob.GetBlobContent(-1) - require.NoError(t, err) - require.Empty(t, r) - - r, err = testBlob.GetBlobContent(4) - require.NoError(t, err) - require.Equal(t, "file", r) - - r, err = testBlob.GetBlobContent(6) - require.NoError(t, err) - require.Equal(t, "file2\n", r) - }) - t.Run("GetContentBase64", func(t *testing.T) { r, err := testBlob.GetContentBase64(100) require.NoError(t, err) @@ -140,11 +122,6 @@ func TestBlob(t *testing.T) { nonExistingBlob, err := repo.GetBlob("00003ff740f9380390d5c9ddef4af18690000000") require.NoError(t, err) - r, err := nonExistingBlob.GetBlobContent(100) - require.Error(t, err) - require.IsType(t, ErrNotExist{}, err) - require.Empty(t, r) - rc, size, err := nonExistingBlob.NewTruncatedReader(100) require.Error(t, err) require.IsType(t, ErrNotExist{}, err) diff --git a/routers/web/repo/view.go b/routers/web/repo/view.go index 3b11d73390..5edf1163ae 100644 --- a/routers/web/repo/view.go +++ b/routers/web/repo/view.go @@ -601,6 +601,7 @@ func renderFile(ctx *context.Context, entry *git.TreeEntry) { ctx.Data["EscapeStatus"] = status ctx.Data["FileContent"] = fileContent ctx.Data["LineEscapeStatus"] = statuses + ctx.Data["IsCitationFile"] = isCitationFile(entry) } if !fInfo.isLFSFile { if ctx.Repo.CanEnableEditor(ctx, ctx.Doer) { @@ -778,6 +779,10 @@ func checkHomeCodeViewable(ctx *context.Context) { ctx.NotFound("Home", errors.New(ctx.Locale.TrString("units.error.no_unit_allowed_repo"))) } +func isCitationFile(entry *git.TreeEntry) bool { + return entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" +} + func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) { if entry.Name() != "" { return @@ -793,16 +798,9 @@ func checkCitationFile(ctx *context.Context, entry *git.TreeEntry) { return } for _, entry := range allEntries { - if entry.Name() == "CITATION.cff" || entry.Name() == "CITATION.bib" { - // Read Citation file contents - if content, err := entry.Blob().GetBlobContent(setting.UI.MaxDisplayFileSize); err != nil { - log.Error("checkCitationFile: GetBlobContent: %v", err) - } else { - ctx.Data["CitationExist"] = true - ctx.Data["CitationFile"] = entry.Name() - ctx.PageData["citationFileContent"] = content - break - } + if isCitationFile(entry) { + ctx.Data["CitationFile"] = entry.Name() + break } } } diff --git a/templates/repo/cite/cite_buttons.tmpl b/templates/repo/cite/cite_buttons.tmpl deleted file mode 100644 index 5a6de23c5c..0000000000 --- a/templates/repo/cite/cite_buttons.tmpl +++ /dev/null @@ -1,8 +0,0 @@ - -BibTeX - - - - diff --git a/templates/repo/cite/cite_modal.tmpl b/templates/repo/cite/cite_modal.tmpl deleted file mode 100644 index 1ce959a5c5..0000000000 --- a/templates/repo/cite/cite_modal.tmpl +++ /dev/null @@ -1,20 +0,0 @@ - diff --git a/templates/repo/home.tmpl b/templates/repo/home.tmpl index d72b8aaa32..eb27af5d54 100644 --- a/templates/repo/home.tmpl +++ b/templates/repo/home.tmpl @@ -130,8 +130,8 @@ {{svg "octicon-file-zip" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_tar"}} {{svg "octicon-package" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.download_bundle"}} {{end}} - {{if .CitationExist}} - {{svg "octicon-cross-reference" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} + {{if .CitationFile}} + {{svg "octicon-cross-reference" 16 "tw-mr-2"}}{{ctx.Locale.Tr "repo.cite_this_repo"}} {{end}} {{range .OpenWithEditorApps}} {{.IconHTML}}{{ctx.Locale.Tr "repo.open_with_editor" .DisplayName}} @@ -140,7 +140,6 @@ {{template "repo/clone_script" .}}{{/* the script will update `.js-clone-url` and related elements */}} - {{template "repo/cite/cite_modal" .}} {{end}} {{if and (not $isHomepage) (not .IsViewFile) (not .IsBlame)}}{{/* IsViewDirectory (not home), TODO: split the templates, avoid using "if" tricks */}}
diff --git a/templates/repo/view_file.tmpl b/templates/repo/view_file.tmpl index 65be791405..411abfa7b4 100644 --- a/templates/repo/view_file.tmpl +++ b/templates/repo/view_file.tmpl @@ -26,9 +26,9 @@ {{end}}

-
+
{{if .ReadmeInList}} - {{svg "octicon-book" 16 "tw-mr-2"}} + {{svg "octicon-book"}} {{.FileName}} {{else}} {{template "repo/file_info" .}} @@ -150,6 +150,9 @@ {{else}} + {{if .IsCitationFile}} + + {{end}} {{range $idx, $code := .FileContent}} @@ -164,6 +167,9 @@ {{end}}
+ {{if .IsCitationFile}} +
+ {{end}}
{{if $.Permission.CanRead $.UnitTypeIssues}} {{ctx.Locale.Tr "repo.issues.context.reference_issue"}} diff --git a/tests/e2e/repo-viewer.test.e2e.ts b/tests/e2e/repo-viewer.test.e2e.ts new file mode 100644 index 0000000000..39e3d1d5b8 --- /dev/null +++ b/tests/e2e/repo-viewer.test.e2e.ts @@ -0,0 +1,47 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: GPL-3.0-or-later + +// @watch start +// web_src/js/webcomponents/citation-information.js +// @watch end + +import {expect} from '@playwright/test'; +import {test} from './utils_e2e.ts'; + +test('CITATION.cff switch', async ({page}) => { + const previewPath = '/user2/rendering-test/src/branch/master/CITATION.cff'; + + const response = await page.goto(previewPath); + expect(response?.status()).toBe(200); + + await expect(page.getByText('cff-version: 1.2.0')).toBeVisible(); + + await page.getByRole('button', {name: 'BibTeX'}).click(); + await expect(page.getByText('cff-version: 1.2.0')).toBeHidden(); + await expect( + page.getByText('howpublished = {https://forgejo.org/},'), + ).toBeVisible(); + + await page.getByRole('button', {name: 'Citation File Format'}).click(); + await expect(page.getByText('cff-version: 1.2.0')).toBeVisible(); +}); + +test('glb file with 3D rendering', async ({page}, workerInfo) => { + test.skip( + workerInfo.project.name !== 'chromium', + 'needs some investigation to run on other platforms', + // https://codeberg.org/forgejo/forgejo/actions/runs/113344/jobs/3/attempt/1 + ); + + const previewPath = + '/user2/rendering-test/src/branch/master/Unicode❤♻Test.glb'; + + const response = await page.goto(previewPath); + expect(response?.status()).toBe(200); + + await page + .getByRole('img', { + name: '3D model. Use mouse, touch or arrow keys to move.', + }) + .click(); +}); diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/32/b0f4a0e8e3f701bf47538436eee8caee863e0e b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/32/b0f4a0e8e3f701bf47538436eee8caee863e0e new file mode 100644 index 0000000000..b7ad82e28a Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/32/b0f4a0e8e3f701bf47538436eee8caee863e0e differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/59/d5df6b2b9c8053cf720403d5716b8ce4a2c6ec b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/59/d5df6b2b9c8053cf720403d5716b8ce4a2c6ec new file mode 100644 index 0000000000..5c6c78aaa5 Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/59/d5df6b2b9c8053cf720403d5716b8ce4a2c6ec differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/74/9b58220a05257c538e036c6196f679f6ccc682 b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/74/9b58220a05257c538e036c6196f679f6ccc682 new file mode 100644 index 0000000000..385fabe8ea Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/74/9b58220a05257c538e036c6196f679f6ccc682 differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/ad/93f47c782e0e2d9d4c05e7e41114de929683e8 b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/ad/93f47c782e0e2d9d4c05e7e41114de929683e8 new file mode 100644 index 0000000000..425444f3ca Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/ad/93f47c782e0e2d9d4c05e7e41114de929683e8 differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/196c874f13227602a2b680c30eef433036e213 b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/196c874f13227602a2b680c30eef433036e213 new file mode 100644 index 0000000000..0921052858 Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/196c874f13227602a2b680c30eef433036e213 differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/27e62cc9ac9db89f6716ee991c8f16507d87e6 b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/27e62cc9ac9db89f6716ee991c8f16507d87e6 new file mode 100644 index 0000000000..91543e5216 Binary files /dev/null and b/tests/gitea-repositories-meta/user2/rendering-test.git/objects/e8/27e62cc9ac9db89f6716ee991c8f16507d87e6 differ diff --git a/tests/gitea-repositories-meta/user2/rendering-test.git/refs/heads/master b/tests/gitea-repositories-meta/user2/rendering-test.git/refs/heads/master index 69d0f8e29c..0e5299ddca 100644 --- a/tests/gitea-repositories-meta/user2/rendering-test.git/refs/heads/master +++ b/tests/gitea-repositories-meta/user2/rendering-test.git/refs/heads/master @@ -1 +1 @@ -fafaad77cb54665ac800d1bf77e6a55bd355eabc +e8196c874f13227602a2b680c30eef433036e213 diff --git a/tests/integration/api_packages_cargo_test.go b/tests/integration/api_packages_cargo_test.go index a72ae4a394..8a7f05d6d3 100644 --- a/tests/integration/api_packages_cargo_test.go +++ b/tests/integration/api_packages_cargo_test.go @@ -89,10 +89,13 @@ func testPackageCargo(t *testing.T, _ *neturl.URL) { blob, err := commit.GetBlobByPath(path) require.NoError(t, err) - content, err := blob.GetBlobContent(1024) + rc, _, err := blob.NewTruncatedReader(1024) require.NoError(t, err) - return content + content, err := io.ReadAll(rc) + require.NoError(t, err) + + return string(content) } root := fmt.Sprintf("%sapi/packages/%s/cargo", setting.AppURL, user.Name) diff --git a/tests/integration/repo_citation_test.go b/tests/integration/repo_citation_test.go index 7a3251d987..7925966f4f 100644 --- a/tests/integration/repo_citation_test.go +++ b/tests/integration/repo_citation_test.go @@ -31,49 +31,58 @@ func TestCitation(t *testing.T) { repo, _, f := tests.CreateDeclarativeRepo(t, user, "citation-no-citation", []unit_model.Type{unit_model.TypeCode}, nil, nil) defer f() - testCitationButtonExists(t, session, repo, "", false) + testCitationButtonExists(t, session, repo, "") }) t.Run("cff citation", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - repo, f := createRepoWithEmptyFile(t, user, "citation-cff", "CITATION.cff") + repo, f := createRepoWithDummyFile(t, user, "citation-cff", "CITATION.cff") defer f() - testCitationButtonExists(t, session, repo, "CITATION.cff", true) + testCitationButtonExists(t, session, repo, "CITATION.cff") }) t.Run("bib citation", func(t *testing.T) { defer tests.PrintCurrentTest(t)() - repo, f := createRepoWithEmptyFile(t, user, "citation-bib", "CITATION.bib") + repo, f := createRepoWithDummyFile(t, user, "citation-bib", "CITATION.bib") defer f() - testCitationButtonExists(t, session, repo, "CITATION.bib", true) + testCitationButtonExists(t, session, repo, "CITATION.bib") }) }) } -func testCitationButtonExists(t *testing.T, session *TestSession, repo *repo_model.Repository, file string, exists bool) { +func testCitationButtonExists(t *testing.T, session *TestSession, repo *repo_model.Repository, file string) { req := NewRequest(t, "GET", repo.HTMLURL()) resp := session.MakeRequest(t, req, http.StatusOK) doc := NewHTMLParser(t, resp.Body) - doc.AssertElement(t, "#cite-repo-button", exists) - - if exists { - href, exists := doc.doc.Find("#goto-citation-btn").Attr("href") - assert.True(t, exists) - - assert.True(t, strings.HasSuffix(href, file)) + links := doc.Find("a.citation-link") + if file == "" { + assert.Equal(t, 0, links.Length()) + return } + + assert.Equal(t, 1, links.Length()) + href, exists := links.Attr("href") + assert.True(t, exists) + assert.True(t, strings.HasSuffix(href, file)) + + // request the citation file to check for webcomponent presence + req = NewRequest(t, "GET", href) + resp = session.MakeRequest(t, req, http.StatusOK) + doc = NewHTMLParser(t, resp.Body) + doc.AssertElement(t, `lazy-webc[tag="citation-information"]`, true) } -func createRepoWithEmptyFile(t *testing.T, user *user_model.User, repoName, fileName string) (*repo_model.Repository, func()) { +func createRepoWithDummyFile(t *testing.T, user *user_model.User, repoName, fileName string) (*repo_model.Repository, func()) { repo, _, f := tests.CreateDeclarativeRepo(t, user, repoName, []unit_model.Type{unit_model.TypeCode}, nil, []*files_service.ChangeRepoFile{ { - Operation: "create", - TreePath: fileName, + Operation: "create", + TreePath: fileName, + ContentReader: strings.NewReader("citation-content"), // viewer requires some content }, }) diff --git a/web_src/css/modules/animations.css b/web_src/css/modules/animations.css index 88b128c643..a820787b16 100644 --- a/web_src/css/modules/animations.css +++ b/web_src/css/modules/animations.css @@ -3,6 +3,10 @@ 100% { transform: translate(-50%, -50%) rotate(360deg); } } +lazy-webc { + display: block; +} + lazy-webc, .is-loading { pointer-events: none !important; diff --git a/web_src/css/repo.css b/web_src/css/repo.css index 3d47ffece1..d51b340e0f 100644 --- a/web_src/css/repo.css +++ b/web_src/css/repo.css @@ -438,6 +438,10 @@ pdf-object { justify-content: center; } +citation-information .tab:not(.active) { + display: none; +} + .repository.file.list .non-diff-file-content .plain-text { padding: 1em 2em; } @@ -1897,48 +1901,6 @@ details.repo-search-result summary::marker { font-weight: var(--font-weight-medium); } -#cite-repo-modal #citation-panel { - display: flex; - width: 100%; -} - -#cite-repo-modal #citation-panel input { - border-radius: 0; - padding: 5px 10px; - width: 50%; - line-height: 1.4; -} - -#cite-repo-modal #citation-panel #citation-copy-content { - border-radius: 0; - padding: 5px 10px; - font-size: 1.2em; - line-height: 1.4; - flex: 1; -} - -#cite-repo-modal #citation-panel #citation-copy-bibtex { - font-size: 13px; - padding: 7.5px 5px; - border-right: none; -} - -#cite-repo-modal #citation-panel #goto-citation-btn { - border-left: none; -} - -#cite-repo-modal #citation-panel > :first-child { - border-radius: var(--border-radius) 0 0 var(--border-radius) !important; -} - -#cite-repo-modal #citation-panel > :last-child { - border-radius: 0 var(--border-radius) var(--border-radius) 0 !important; -} - -#cite-repo-modal #citation-panel .icon.button { - padding: 0 10px; -} - #search-user-box .results .result .image { order: 0; margin-right: 12px; @@ -2381,6 +2343,7 @@ tbody.commit-list { overflow-x: auto; padding: 6px 12px !important; font-size: 13px !important; + min-height: 46px; } .file-info { diff --git a/web_src/js/features/citation.js b/web_src/js/features/citation.js deleted file mode 100644 index 7e26bff276..0000000000 --- a/web_src/js/features/citation.js +++ /dev/null @@ -1,50 +0,0 @@ -import $ from 'jquery'; -import {getCurrentLocale} from '../utils.js'; - -const {pageData} = window.config; - -async function initInputCitationValue(inputContent) { - const [{Cite, plugins}] = await Promise.all([ - import(/* webpackChunkName: "citation-js-core" */'@citation-js/core'), - import(/* webpackChunkName: "citation-js-formats" */'@citation-js/plugin-software-formats'), - import(/* webpackChunkName: "citation-js-bibtex" */'@citation-js/plugin-bibtex'), - ]); - const {citationFileContent} = pageData; - const config = plugins.config.get('@bibtex'); - config.constants.fieldTypes.doi = ['field', 'literal']; - config.constants.fieldTypes.version = ['field', 'literal']; - const citationFormatter = new Cite(citationFileContent); - const lang = getCurrentLocale() || 'en-US'; - const bibtexOutput = citationFormatter.format('bibtex', {lang}); - inputContent.value = bibtexOutput; -} - -export async function initCitationFileCopyContent() { - if (!pageData.citationFileContent) return; - - const inputContent = document.getElementById('citation-copy-content'); - - if (!inputContent) return; - - document.getElementById('cite-repo-button')?.addEventListener('click', async (e) => { - const dropdownBtn = e.target.closest('.ui.dropdown.button'); - dropdownBtn.classList.add('is-loading'); - - try { - try { - await initInputCitationValue(inputContent); - } catch (e) { - console.error(`initCitationFileCopyContent error: ${e}`, e); - return; - } - - inputContent.addEventListener('click', () => { - inputContent.select(); - }); - } finally { - dropdownBtn.classList.remove('is-loading'); - } - - $('#cite-repo-modal').modal('show'); - }); -} diff --git a/web_src/js/features/repo-legacy.js b/web_src/js/features/repo-legacy.js index 74efdaa530..05766c6c78 100644 --- a/web_src/js/features/repo-legacy.js +++ b/web_src/js/features/repo-legacy.js @@ -13,7 +13,6 @@ import {initRepoBranchTagSelector} from './repo-branch-tag-selector.js'; import { initRepoCloneLink, initRepoCommonBranchOrTagDropdown, initRepoCommonFilterSearchDropdown, } from './repo-common.js'; -import {initCitationFileCopyContent} from './citation.js'; import {initCompLabelEdit} from './comp/LabelEdit.js'; import {initRepoDiffConversationNav} from './repo-diff.js'; import {showErrorToast} from '../modules/toast.js'; @@ -457,7 +456,6 @@ export function initRepository() { } initRepoCloneLink(); - initCitationFileCopyContent(); initRepoSettingBranches(); // Issues diff --git a/web_src/js/webcomponents/citation-information.js b/web_src/js/webcomponents/citation-information.js new file mode 100644 index 0000000000..6ea9fdcc0c --- /dev/null +++ b/web_src/js/webcomponents/citation-information.js @@ -0,0 +1,114 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +import '@citation-js/plugin-software-formats'; +import '@citation-js/plugin-bibtex'; +import {Cite, plugins} from '@citation-js/core'; + +import {getCurrentLocale} from '../utils.js'; +import {initTab} from '../modules/tab.ts'; + +window.customElements.define( + 'citation-information', + class extends HTMLElement { + connectedCallback() { + const children = this.children; // eslint-disable-line wc/no-child-traversal-in-connectedcallback + if (children.length !== 1) { + // developer error + throw new Error( + ` expected one child, got ${children.length}`, + ); + } + + const lang = getCurrentLocale() || 'en-US'; + + const raw = children[0]; + raw.dataset.tab = 'raw'; + raw.classList.add('tab', 'active'); + + // like in copy-content + const lineEls = raw.querySelectorAll('.lines-code'); + const code = Array.from(lineEls, (el) => el.textContent).join(''); + + const inputType = plugins.input.type(code); + let parsed; + try { + parsed = new Cite(code, {forceType: inputType}); + } catch (err) { + const elContainer = document.createElement('div'); + elContainer.classList.add('ui', 'warning', 'message'); + + const elHeader = document.createElement('div'); + elHeader.classList.add('header'); + elHeader.textContent = `Could not parse citation-information (format ${inputType})`; // ideally this message should be localized, however the error below will likely be in english + elContainer.append(elHeader); + + const elParagraph = document.createElement('pre'); + elParagraph.textContent = err; + elContainer.append(elParagraph); + this.prepend(elContainer); + return; + } + + const toggleBar = document.createElement('div'); + toggleBar.classList.add('switch'); + + const newButton = (txt, id, tooltip, active) => { + const el = document.createElement('button'); + el.textContent = txt; + el.dataset.tab = id; + if (tooltip) { + el.dataset.tooltipContent = tooltip; + } + el.classList.add('item'); + if (active) { + el.classList.add('active'); + } + return el; + }; + let originalText = 'Original'; + let originalTooltip = ''; + switch (inputType) { + case '@biblatex/text': + originalText = 'BibTeX'; + break; + case '@else/yaml': + originalText = 'CFF'; + originalTooltip = 'Citation File Format'; + break; + } + toggleBar.append(newButton(originalText, 'raw', originalTooltip, true)); + + const appendTab = (id, btnLabel, btnTooltip, tabContent) => { + const el = document.createElement('pre'); + el.textContent = tabContent; + el.dataset.tab = id; + el.classList.add('tab'); + el.style.padding = '1rem'; + el.style.margin = 0; + this.append(el); + toggleBar.append(newButton(btnLabel, id, btnTooltip)); + }; + if (inputType !== '@biblatex/text') { + appendTab( + 'bibtex', + 'BibTeX', + '', + parsed.format('bibtex', {lang}).trim(), + ); + } + if (inputType !== '@else/yaml') { + appendTab( + 'cff', + 'CFF', + 'Citation File Format', + parsed.format('cff', {lang}).trim(), + ); + } + + const toggleBarParent = document.querySelector('.file-header-left'); + toggleBarParent.prepend(toggleBar); + initTab(toggleBarParent); + } + }, +); diff --git a/web_src/js/webcomponents/lazy-webc.js b/web_src/js/webcomponents/lazy-webc.js index 3570df3b5d..e1a8513d8d 100644 --- a/web_src/js/webcomponents/lazy-webc.js +++ b/web_src/js/webcomponents/lazy-webc.js @@ -1,3 +1,6 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + import {onDomReady} from '../utils/dom.js'; /** @@ -22,6 +25,9 @@ const loadableComponents = { 'pdf-object': lazyPromise(() => { return import(/* webpackChunkName: "pdf-object" */ './pdf-object.js'); }), + 'citation-information': lazyPromise(() => { + return import(/* webpackChunkName: "citation-information" */ './citation-information.js'); + }), }; /**