Skip to content

feat(download): 搜索框支持识别资源链接#2989

Open
kakala666 wants to merge 8 commits into
PCL-Community:devfrom
kakala666:feat/search-resource-link
Open

feat(download): 搜索框支持识别资源链接#2989
kakala666 wants to merge 8 commits into
PCL-Community:devfrom
kakala666:feat/search-resource-link

Conversation

@kakala666
Copy link
Copy Markdown
Contributor

@kakala666 kakala666 commented Jun 1, 2026

Close #2942

在下载页搜索框输入恰好 1 条 CurseForge/Modrinth 资源链接并搜索时,识别该链接并将对应资源作为单条结果显示,便于定位用关键词搜不到的资源。

  • 输入恰好 1 条 CF/Modrinth 链接时显示该资源,0 条或多条链接照常按文本搜索
  • 解析失败时提示「未能识别或获取该资源链接」,资源类型与当前页不符时按无结果处理
  • 复用现有链接解析逻辑,融入搜索框,未新增按钮或其他 UI

Summary by Sourcery

在下载搜索框中支持识别单个 CurseForge/Modrinth 资源链接,并重构链接到项目的解析逻辑以便复用。

新功能:

  • 允许下载页面的搜索框在输入中检测单个 CurseForge 或 Modrinth 资源链接,并将该资源作为专门的搜索结果返回。

增强:

  • 抽取共享逻辑,用于将 CurseForge/Modrinth 链接解析为项目 ID,以便在剪贴板处理和搜索中复用。
  • 新增逻辑,使基于链接的搜索在行为上与现有的“无结果”和错误状态保持一致,包括类型过滤和分页处理。
Original summary in English

Summary by Sourcery

Support recognizing single CurseForge/Modrinth resource links in the download search box and refactor link-to-project resolution for reuse.

New Features:

  • Allow the download page search box to detect a single CurseForge or Modrinth resource link in the input and return that resource as a dedicated search result.

Enhancements:

  • Extract shared logic to resolve CurseForge/Modrinth links to project IDs so it can be reused by clipboard handling and search.
  • Add logic to interpret link-based searches consistently with existing no-results and error states, including type filtering and pagination handling.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: L PR 大小评估:大型 labels Jun 1, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented Jun 1, 2026

审阅者指南

将 CurseForge/Modrinth 链接解析重构为可复用的辅助方法,并将“单链接识别”整合进下载搜索流程,使在搜索框中输入恰好一个资源 URL 时,能够将其解析为一个单独的搜索结果并返回对应项目,同时具备完善的错误提示与类型校验。

下载搜索中单个资源链接处理的序列图

sequenceDiagram
    actor User
    participant DownloadSearchBox as DownloadSearch
    participant ModComp
    participant CompProjectStorage
    participant CompRequest
    participant ModDownload
    participant Lang

    User->>DownloadSearch: enter searchText (single CF/Modrinth link)
    DownloadSearch->>ModComp: CompProjectsGet(request, task)
    ModComp->>CompProjectStorage: TryExtractSingleResourceLink(request.searchText)
    CompProjectStorage-->>ModComp: singleLink

    ModComp->>CompProjectStorage: ResolveLinkToProjectId(singleLink)
    alt CurseForge link
        CompProjectStorage->>ModDownload: DlModRequest(url with slug)
        ModDownload-->>CompProjectStorage: JsonObject (CF search result)
    else Modrinth link
        CompProjectStorage->>ModDownload: DlModRequest(url with slug)
        ModDownload-->>CompProjectStorage: JsonObject (Modrinth project)
    end
    CompProjectStorage-->>ModComp: projectId or null

    alt projectId resolved
        ModComp->>CompRequest: GetCompProjectsByIds([projectId])
        CompRequest-->>ModComp: CompProject or null
        alt CompProject is null
            ModComp->>Lang: Text(Download.Comp.Link.ResolveFailed)
            Lang-->>ModComp: message
            ModComp-->>DownloadSearch: throw Exception(message)
        else type mismatch with request.type
            ModComp->>Lang: Text(Download.Comp.List.NoMatchingResults)
            Lang-->>ModComp: message
            ModComp-->>DownloadSearch: throw Exception(message)
        else valid single result
            ModComp->>DownloadSearch: storage.results.Add(project)
            ModComp->>DownloadSearch: curseForgeTotal = 0, modrinthTotal = 0
        end
    else projectId null or resolve error
        ModComp->>Lang: Text(Download.Comp.Link.ResolveFailed)
        Lang-->>ModComp: message
        ModComp-->>DownloadSearch: throw Exception(message)
    end
Loading

文件级变更

Change Details Files
将 CurseForge/Modrinth 链接解析逻辑重构为可复用方法
  • 从原有剪贴板处理逻辑中抽取链接解析代码为静态方法 ResolveLinkToProjectId(url),统一处理 CurseForge / Modrinth 链接到 projectId 的解析逻辑
  • 在 CurseForge 分支保留原有 slug / classId 检查与二次请求逻辑;在 Modrinth 分支中按 slug 调用 v2 project 接口获取 id
  • GetClipboardResource 中使用新的 ResolveLinkToProjectId(text) 替换内联解析代码,保持原有行为不变
Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
在下载搜索流程中识别单条资源链接并直接返回对应项目
  • 新增 TryExtractSingleResourceLink(text) 方法,仅在输入文本中恰好包含 1 条 CurseForge/Modrinth 资源链接时返回该链接,否则返回 null
  • CompProjectsGet 中优先调用 TryExtractSingleResourceLink(request.searchText),若命中则通过 ResolveLinkToProjectId + CompRequest.GetCompProjectsByIds 获取资源
  • 当解析失败或通过 ID 获取资源失败时,抛出本地化异常 Download.Comp.Link.ResolveFailed;当资源类型与当前页不匹配时,抛出 Download.Comp.List.NoMatchingResults,以沿用现有“无结果”提示行为
  • 命中单条资源时,仅向 storage.results 添加该项目,并将 curseForgeTotalmodrinthTotal 置为 0 以隐藏分页,然后提前 return
Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
为新的错误提示与功能补充本地化文案
  • zh-CNen-US 语言资源文件中新增 Download.Comp.Link.ResolveFailed 和相关提示字符串,用于“未能识别或获取该资源链接”的场景
PCL.Core/App/Localization/Languages/en-US.xaml
PCL.Core/App/Localization/Languages/zh-CN.xaml

与关联 Issue 的符合度评估

Issue Objective Addressed Explanation
#2942 在资源下载页中支持通过直接粘贴 CurseForge/Modrinth 资源链接来定位并显示对应资源(可通过搜索框实现,无需额外按钮)。
#2942 支持从多行文本中识别并解析多条 CurseForge/Modrinth 链接,生成对应资源列表进行展示。 本 PR 仅在搜索框中识别“恰好 1 条”链接,将其解析为单个资源结果;当有 0 条或多条链接时,仍按普通文本搜索,并未实现对多条链接的批量识别与列表展示。

可能相关的 Issue


提示与命令

与 Sourcery 交互

  • 触发新评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub Issue: 在某条评审评论下回复,请求 Sourcery 从该评论创建一个 issue。你也可以直接回复 @sourcery-ai issue,从该评论生成 issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写入 @sourcery-ai,可随时生成标题。也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 内容中任意位置写入 @sourcery-ai summary,即可在指定位置生成 PR 摘要。也可以在 pull request 中评论 @sourcery-ai summary 来(重新)生成摘要。
  • 生成审阅者指南: 在 pull request 中评论 @sourcery-ai guide,可随时(重新)生成审阅者指南。
  • 一次性解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve 来解决所有 Sourcery 评论。如果你已经处理完所有评论且不想再看到它们,这会很有帮助。
  • 忽略所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss 来忽略所有现有的 Sourcery 评审。尤其适用于你希望从一次全新的评审开始的情况——别忘了再评论一次 @sourcery-ai review 来触发新的评审!

自定义你的体验

访问你的 dashboard 以:

  • 启用或禁用诸如 Sourcery 自动生成的 pull request 摘要、审阅者指南等评审功能。
  • 修改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

获取帮助

Original review guide in English

Reviewer's Guide

Refactors CurseForge/Modrinth link parsing into reusable helpers and integrates single-link detection into the download search flow so entering exactly one resource URL returns that specific project as a single search result, with proper error messaging and type validation.

Sequence diagram for single resource link handling in download search

sequenceDiagram
    actor User
    participant DownloadSearchBox as DownloadSearch
    participant ModComp
    participant CompProjectStorage
    participant CompRequest
    participant ModDownload
    participant Lang

    User->>DownloadSearch: enter searchText (single CF/Modrinth link)
    DownloadSearch->>ModComp: CompProjectsGet(request, task)
    ModComp->>CompProjectStorage: TryExtractSingleResourceLink(request.searchText)
    CompProjectStorage-->>ModComp: singleLink

    ModComp->>CompProjectStorage: ResolveLinkToProjectId(singleLink)
    alt CurseForge link
        CompProjectStorage->>ModDownload: DlModRequest(url with slug)
        ModDownload-->>CompProjectStorage: JsonObject (CF search result)
    else Modrinth link
        CompProjectStorage->>ModDownload: DlModRequest(url with slug)
        ModDownload-->>CompProjectStorage: JsonObject (Modrinth project)
    end
    CompProjectStorage-->>ModComp: projectId or null

    alt projectId resolved
        ModComp->>CompRequest: GetCompProjectsByIds([projectId])
        CompRequest-->>ModComp: CompProject or null
        alt CompProject is null
            ModComp->>Lang: Text(Download.Comp.Link.ResolveFailed)
            Lang-->>ModComp: message
            ModComp-->>DownloadSearch: throw Exception(message)
        else type mismatch with request.type
            ModComp->>Lang: Text(Download.Comp.List.NoMatchingResults)
            Lang-->>ModComp: message
            ModComp-->>DownloadSearch: throw Exception(message)
        else valid single result
            ModComp->>DownloadSearch: storage.results.Add(project)
            ModComp->>DownloadSearch: curseForgeTotal = 0, modrinthTotal = 0
        end
    else projectId null or resolve error
        ModComp->>Lang: Text(Download.Comp.Link.ResolveFailed)
        Lang-->>ModComp: message
        ModComp-->>DownloadSearch: throw Exception(message)
    end
Loading

File-Level Changes

Change Details Files
Refactor CurseForge/Modrinth链接解析逻辑为可复用方法
  • 抽取原剪贴板处理中的链接解析代码为 ResolveLinkToProjectId(url) 静态方法,统一处理 CurseForge / Modrinth 链接到 projectId 的解析逻辑
  • 在 CurseForge 分支保留原有 slug / classId 检查与二次请求逻辑,在 Modrinth 分支按 slug 调用 v2 project 接口获取 id
  • 在 GetClipboardResource 中使用新的 ResolveLinkToProjectId(text) 替换内联解析代码,保持原有行为不变
Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
在下载搜索流程中识别单条资源链接并直接返回对应项目
  • 新增 TryExtractSingleResourceLink(text) 方法,仅在输入文本中恰好包含 1 条 CurseForge/Modrinth 资源链接时返回该链接,否则返回 null
  • 在 CompProjectsGet 中优先调用 TryExtractSingleResourceLink(request.searchText),若命中则通过 ResolveLinkToProjectId + CompRequest.GetCompProjectsByIds 获取资源
  • 当解析失败或通过 ID 获取资源失败时抛出本地化异常 Download.Comp.Link.ResolveFailed;当资源类型与当前页不匹配时抛出 Download.Comp.List.NoMatchingResults 以沿用现有无结果提示行为
  • 命中单条资源时,仅向 storage.results 添加该项目并将 curseForgeTotal 与 modrinthTotal 置 0 以隐藏分页,然后提前 return
Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
为新错误提示与功能补充本地化文案
  • 在 zh-CN 与 en-US 语言资源文件中新增 Download.Comp.Link.ResolveFailed 和相关提示字符串,用于“未能识别或获取该资源链接”场景
PCL.Core/App/Localization/Languages/en-US.xaml
PCL.Core/App/Localization/Languages/zh-CN.xaml

Assessment against linked issues

Issue Objective Addressed Explanation
#2942 在资源下载页中支持通过直接粘贴 CurseForge/Modrinth 资源链接来定位并显示对应资源(可通过搜索框实现,无需额外按钮)。
#2942 支持从多行文本中识别并解析多条 CurseForge/Modrinth 链接,生成对应资源的列表进行展示。 PR 仅在搜索框中识别“恰好 1 条”链接,将其解析为单个资源结果;当有 0 条或多条链接时,仍按普通文本搜索,并未实现对多条链接的批量识别与列表展示。

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - 我在这里给出了一些整体性的反馈:

  • TryExtractSingleResourceLink 中,仅按空白字符分割可能会遗漏一些常见情况,比如链接后紧跟标点符号(, ,, ), 等);建议在提取前去掉常见的结尾标点,或者使用对 URL 更友好的抽取方式,以便在自然文本中也能可靠地工作。
  • TryExtractSingleResourceLinkResolveLinkToProjectId 中,URL 预处理(Replace("https://", "").Replace("http://", ""))以及主机名检查是重复的;可以考虑抽取到一个共享的 helper 中(或使用 Uri 解析),这样能够降低未来出现不一致的风险。
  • ResolveLinkToProjectId 中的 categoryMapping 字典在每次调用时都会重新分配;将其改为 static readonly 字段可以避免在频繁查询时产生不必要的分配。
供 AI Agent 使用的提示词
Please address the comments from this code review:

## Overall Comments
-`TryExtractSingleResourceLink` 中,仅按空白字符分割可能会遗漏一些常见情况,比如链接后紧跟标点符号(``, `,`, `)`, 等);建议在提取前去掉常见的结尾标点,或者使用对 URL 更友好的抽取方式,以便在自然文本中也能可靠地工作。
-`TryExtractSingleResourceLink``ResolveLinkToProjectId` 中,URL 预处理(`Replace("https://", "").Replace("http://", "")`)以及主机名检查是重复的;可以考虑抽取到一个共享的 helper 中(或使用 `Uri` 解析),这样能够降低未来出现不一致的风险。
- `ResolveLinkToProjectId` 中的 `categoryMapping` 字典在每次调用时都会重新分配;将其改为 static readonly 字段可以避免在频繁查询时产生不必要的分配。

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈来改进后续的评审。
Original comment in English

Hey - I've left some high level feedback:

  • In TryExtractSingleResourceLink, splitting only on whitespace may miss common cases like links followed by punctuation (, ,, ), etc.); consider trimming common trailing punctuation or using a more URL-aware extraction so the feature works reliably in natural text.
  • The URL preprocessing (Replace("https://", "").Replace("http://", "")) and host checks are duplicated between TryExtractSingleResourceLink and ResolveLinkToProjectId; factoring this into a shared helper (or using Uri parsing) would reduce the risk of future inconsistencies.
  • The categoryMapping dictionary in ResolveLinkToProjectId is allocated on every call; making it a static readonly field would avoid unnecessary allocations on frequent searches.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `TryExtractSingleResourceLink`, splitting only on whitespace may miss common cases like links followed by punctuation (``, `,`, `)`, etc.); consider trimming common trailing punctuation or using a more URL-aware extraction so the feature works reliably in natural text.
- The URL preprocessing (`Replace("https://", "").Replace("http://", "")`) and host checks are duplicated between `TryExtractSingleResourceLink` and `ResolveLinkToProjectId`; factoring this into a shared helper (or using `Uri` parsing) would reduce the risk of future inconsistencies.
- The `categoryMapping` dictionary in `ResolveLinkToProjectId` is allocated on every call; making it a static readonly field would avoid unnecessary allocations on frequent searches.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@Chiloven945 Chiloven945 requested a review from a team June 2, 2026 11:06
Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs Outdated
kakala666 added 3 commits June 2, 2026 21:50
在下载页搜索框输入恰好 1 条 CurseForge/Modrinth 资源链接时,识别并将该资源作为单条结果显示;0 或多条链接照常文本搜索,解析失败或类型不符则无结果。
@kakala666 kakala666 force-pushed the feat/search-resource-link branch from 5d7515c to 574e437 Compare June 2, 2026 13:54
Copy link
Copy Markdown
Member

@whitecat346 whitecat346 left a comment

Choose a reason for hiding this comment

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

as the comment

{
json = ModDownload.DlModRequest<JsonObject>(
$"https://api.curseforge.com/v1/mods/search?gameId=432&slug={encodedSlug}&classId={targetClassId}");
dataArray = (JsonArray)json["data"];
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

不建议将一个语义不同的值赋值给存在的变量,最佳实践为单开一个新的变量(如innerDatas),否则会导致阅读时的理解错误

dataArray = (JsonArray)json["data"];
}

return dataArray.Any() ? dataArray[0]["id"]?.ToString() : null;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

按照R2239的comment修改后这里应当做出改变,包括前面if分支的逻辑

public static string? TryExtractSingleResourceLink(string? text)
{
if (string.IsNullOrWhiteSpace(text)) return null;
var tokens = text.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

建议使用[]表达式,而不是new [] {}

private static string? FindFirstResourceLinkToken(string? text)
{
if (string.IsNullOrWhiteSpace(text)) return null;
foreach (var token in text.Split(new[] { ' ', '\t', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

建议使用[]表达式,而不是new [] {}

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs Outdated
if (uri.Scheme != Uri.UriSchemeHttp && uri.Scheme != Uri.UriSchemeHttps) return false;

// 仅取 path 段,天然忽略 ?query 与 #fragment
var segments = uri.AbsolutePath.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

建议使用[]表达式,而不是new [] {}

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs Outdated
@whitecat346
Copy link
Copy Markdown
Member

请尽快处理,否则按照超时关闭

kakala666 and others added 4 commits June 5, 2026 23:01
Co-authored-by: WhiteCAT <64885812+whitecat346@users.noreply.github.com>
Co-authored-by: WhiteCAT <64885812+whitecat346@users.noreply.github.com>
集合表达式替代 new[] 写法,classId 重搜结果改用独立变量以区分语义,
并删除新增方法 XMLDoc 的额外缩进以统一注释风格。
@kakala666
Copy link
Copy Markdown
Contributor Author

改好了

@lhx077
Copy link
Copy Markdown
Member

lhx077 commented Jun 5, 2026

@codex

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a74b8de1bf

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b31c44193f

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModComp.cs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: L PR 大小评估:大型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

添加资源的“从链接获取”

5 participants