[Propsal] Dynamical Frontend Render Plugin #14685

Open
opened 2025-11-02 11:19:59 -06:00 by GiteaMirror · 25 comments
Owner

Originally created by @lunny on GitHub (Jun 30, 2025).

Originally assigned to: @kerwin612 on GitHub.

Preface

#34794 introduced a frontend static render plugin machism. I believe we can add more plugins in the future. But that will bloat the binary size and we cannot accept all the possible plugins. So that we need a machism to allow users(instance administrator) to find/install/uninstall Gitea Render Plugins.

Design Goals

There are 5 kinds of renders in Gitea.

  • Render a file in the git/git lfs
  • Render a file in the wiki
  • Render part of content in a markdown file/issue content/comment content
  • Render part of content in a diff compare page (i.e. an image changed between 2 commits)
  • Preview in the web editor

This plugin system should support rendering a file in the git/git lfs at least.

The Gitea Frontend Render Plugin system aims to introduce a flexible, modular architecture that allows instance administrators to extend Gitea’s file rendering capabilities without modifying the core binary. Inspired by modern plugin-based ecosystems (e.g., VSCode extensions, browser plugins), this mechanism empowers the community to develop, share, and deploy custom renderers for specific file types (e.g., Markdown with diagrams, PDF, AsciiDoc, Jupyter notebooks, etc.).

A render plugin is a JavaScript (or TypeScript-transpiled) module that conforms to a defined Gitea rendering interface and is dynamically loaded by the frontend at runtime. It is designed to safely extend the rendering capabilities of Gitea views, in a secure and sandboxed way.

• Pluggability: Support multiple independently developed renderers.
• Discoverability: Administrators should be able to easily discover and install plugins.
• Configurability: Allow instance-level control over which plugins are enabled or disabled.
• Lightweight core: Keep the core Gitea binary minimal by externalizing optional renderers.

Relationship with current renderers

Gitea currently have supported backend renderers for markdown/csv and som other files. And recently Gitea introduced a frontend static plugin system which could render pdf and 3D images. The current plugin system could be kept there and the dynamic plugins mentioned in this proposal could be loaded as a special frontend static plugin. That means creating a plugin in web_src/js/render/plugins/dynamic-plugin.ts. The dynamic plugins will be loaded according to the conditions in that file.

Plugin Format

Each plugin could be a .zip archive or an online git url which containing the following structure:

my-plugin.zip / repository folder
├── manifest.json     # Plugin metadata
├── render.js             # Entrypoint script
├── assets/                # Optional static assets (CSS, icons, fonts, etc.)

manifest.json example:

{
  "id": "pdf-renderer", // unique for every Gitea instance
  "name": "PDF Renderer",
  "version": "1.0.0",
  "description": "Render PDF files directly in the Gitea file view.",
  "entry": "render.js",
  "filePatterns": ["*.pdf"]
}

Plugin Storage and Delivery

To ensure security, performance, and compatibility with Gitea’s deployment model, all frontend render plugins must be downloaded and stored under a specific Gitea-managed directory, and served through Gitea’s internal HTTP router. This design avoids external dependency loading (e.g., from CDN or untrusted domains), ensuring all assets are locally verifiable and cacheable.

Storage Path

Uploaded/Downloaded plugin archives (.zip) will be extracted to a designated path inside the Gitea data/ directory. For example:

<gitea-root>/data/render-plugins/
├── pdf-renderer/
│   ├── manifest.json
│   ├── render.js
│   └── assets/
└── 3d-renderer/
    ├── manifest.json
    └── render.js

This allows Gitea to:
• Serve static assets securely via router
• Perform updates or uninstalls by removing plugin directories

Storage of Plugin State

Enabled status is stored in Gitea’s database under a new table (e.g., render_plugin):

type RenderPlugin struct {
   ID int64
   Name string `xorm:"unique"`
   Source string // this could be uploaded or a url
   Version string
   Description string
   Enabled bool
   InstalledAt util.timestamp
   UpdatedAt util.timestamp
}

Serving via Gitea Router

Gitea will expose plugin files through a well-defined route, e.g.

/assets/render-plugins // list all enabled render plugins metadata, this could be generated dynamically when install/uninstall plugins

/assets/render-plugins/pdf-renderer/render.js // main entry of one plugin

/assets/render-plugins/3d-renderer/assets/style.css // possible css file of one plugin

Admin UI Flow

On the Site Administration > Render Plugins page, admins will be able to:
• See a list of all installed plugins
• View plugin details (name, description, version)
• Enable/Disable plugin via toggle switch
• Delete plugin (removes files and DB entry)

Installation Flow

1.	Admin uploads plugin ZIP file or an via Gitea Admin Panel when creating a new plugin or upgrading a plugin
2.	Gitea validates the manifest, extracts it to data/render-plugins/.
3.	Plugin assets become accessible via routes.

Delete Plugin

When delete plugin, it will be removed from disk assets/ directory and database.

Enable/Disable Plugin

When enable/disable plugins, the database will be updated and the plugins meta files will be updated.

Originally created by @lunny on GitHub (Jun 30, 2025). Originally assigned to: @kerwin612 on GitHub. # Preface #34794 introduced a frontend static render plugin machism. I believe we can add more plugins in the future. But that will bloat the binary size and we cannot accept all the possible plugins. So that we need a machism to allow users(instance administrator) to find/install/uninstall Gitea Render Plugins. # Design Goals There are 5 kinds of renders in Gitea. * Render a file in the git/git lfs * Render a file in the wiki * Render part of content in a markdown file/issue content/comment content * Render part of content in a diff compare page (i.e. an image changed between 2 commits) * Preview in the web editor This plugin system should support rendering a file in the git/git lfs at least. The Gitea Frontend Render Plugin system aims to introduce a flexible, modular architecture that allows instance administrators to extend Gitea’s file rendering capabilities without modifying the core binary. Inspired by modern plugin-based ecosystems (e.g., VSCode extensions, browser plugins), this mechanism empowers the community to develop, share, and deploy custom renderers for specific file types (e.g., Markdown with diagrams, PDF, AsciiDoc, Jupyter notebooks, etc.). A render plugin is a JavaScript (or TypeScript-transpiled) module that conforms to a defined Gitea rendering interface and is dynamically loaded by the frontend at runtime. It is designed to safely extend the rendering capabilities of Gitea views, in a secure and sandboxed way. • Pluggability: Support multiple independently developed renderers. • Discoverability: Administrators should be able to easily discover and install plugins. • Configurability: Allow instance-level control over which plugins are enabled or disabled. • Lightweight core: Keep the core Gitea binary minimal by externalizing optional renderers. # Relationship with current renderers Gitea currently have supported backend renderers for markdown/csv and som other files. And recently Gitea introduced a frontend static plugin system which could render pdf and 3D images. The current plugin system could be kept there and the dynamic plugins mentioned in this proposal could be loaded as a special frontend static plugin. That means creating a plugin in `web_src/js/render/plugins/dynamic-plugin.ts`. The dynamic plugins will be loaded according to the conditions in that file. # Plugin Format Each plugin could be a `.zip` archive or an online git url which containing the following structure: ``` my-plugin.zip / repository folder ├── manifest.json # Plugin metadata ├── render.js # Entrypoint script ├── assets/ # Optional static assets (CSS, icons, fonts, etc.) ``` manifest.json example: ```json { "id": "pdf-renderer", // unique for every Gitea instance "name": "PDF Renderer", "version": "1.0.0", "description": "Render PDF files directly in the Gitea file view.", "entry": "render.js", "filePatterns": ["*.pdf"] } ``` # Plugin Storage and Delivery To ensure security, performance, and compatibility with Gitea’s deployment model, all frontend render plugins must be downloaded and stored under a specific Gitea-managed directory, and served through Gitea’s internal HTTP router. This design avoids external dependency loading (e.g., from CDN or untrusted domains), ensuring all assets are locally verifiable and cacheable. ## Storage Path Uploaded/Downloaded plugin archives (.zip) will be extracted to a designated path inside the Gitea data/ directory. For example: ``` <gitea-root>/data/render-plugins/ ├── pdf-renderer/ │ ├── manifest.json │ ├── render.js │ └── assets/ └── 3d-renderer/ ├── manifest.json └── render.js ``` This allows Gitea to: • Serve static assets securely via router • Perform updates or uninstalls by removing plugin directories ## Storage of Plugin State Enabled status is stored in Gitea’s database under a new table (e.g., render_plugin): ``` type RenderPlugin struct { ID int64 Name string `xorm:"unique"` Source string // this could be uploaded or a url Version string Description string Enabled bool InstalledAt util.timestamp UpdatedAt util.timestamp } ``` ## Serving via Gitea Router Gitea will expose plugin files through a well-defined route, e.g. ``` /assets/render-plugins // list all enabled render plugins metadata, this could be generated dynamically when install/uninstall plugins /assets/render-plugins/pdf-renderer/render.js // main entry of one plugin /assets/render-plugins/3d-renderer/assets/style.css // possible css file of one plugin ``` # Admin UI Flow On the Site Administration > Render Plugins page, admins will be able to: • See a list of all installed plugins • View plugin details (name, description, version) • Enable/Disable plugin via toggle switch • Delete plugin (removes files and DB entry) ## Installation Flow 1. Admin uploads plugin ZIP file or an via Gitea Admin Panel when creating a new plugin or upgrading a plugin 2. Gitea validates the manifest, extracts it to data/render-plugins/. 3. Plugin assets become accessible via routes. ## Delete Plugin When delete plugin, it will be removed from disk assets/ directory and database. ## Enable/Disable Plugin When enable/disable plugins, the database will be updated and the plugins meta files will be updated.
GiteaMirror added the topic/content-renderingtype/proposal labels 2025-11-02 11:19:59 -06:00
Author
Owner

@wxiaoguang commented on GitHub (Jul 1, 2025):

It needs to maintain the compatibility correctly. Propose a feasible plan before writing code.

@wxiaoguang commented on GitHub (Jul 1, 2025): It needs to maintain the compatibility correctly. Propose a feasible plan before writing code.
Author
Owner

@lunny commented on GitHub (Jul 1, 2025):

Maybe the current plugin system could be kept there and the dynamic plugins mentioned in this proposal could be loaded as a special plugin. That means creating a plugin in web_src/js/render/plugins/dynamic-plugin.ts. The dynamic plugins will be loaded according to the conditions in that file.

@lunny commented on GitHub (Jul 1, 2025): Maybe the current plugin system could be kept there and the dynamic plugins mentioned in this proposal could be loaded as a special plugin. That means creating a plugin in `web_src/js/render/plugins/dynamic-plugin.ts`. The dynamic plugins will be loaded according to the conditions in that file.
Author
Owner

@silverwind commented on GitHub (Jul 1, 2025):

I think instead of manifest.json, we could leverage the existing package.json spec. The script itself should export a async function as its ESM default export that is imported and called by our code with likely a File blob to render and a destination DOM element to render to. opts for any additional options passed from gitea. Here's an example:

export default async function render(file: File, target: HTMLElement, opts: Record<string, any>): Promise<void> {
  target.innerHTML = "result";
}
@silverwind commented on GitHub (Jul 1, 2025): I think instead of `manifest.json`, we could leverage the existing `package.json` spec. The script itself should export a async function as its ESM default export that is imported and called by our code with likely a `File` blob to render and a destination DOM element to render to. `opts` for any additional options passed from gitea. Here's an example: ```ts export default async function render(file: File, target: HTMLElement, opts: Record<string, any>): Promise<void> { target.innerHTML = "result"; }
Author
Owner

@kerwin612 commented on GitHub (Jul 2, 2025):

Here’s a concrete plan for how this can be implemented in Gitea, step by step:

1. Plugin Packaging

  • Each plugin is a zip archive (or git repo) containing either a manifest.json or package.json, plus a JS entry file (render.js) and optional assets.
  • The manifest file must include:
    • id / name (unique string)
    • version
    • filePatterns (array, e.g. ["*.pdf"]) OR MIME-TYPE???
    • entry (relative path to main JS file)
    • description (optional)

2. Backend: Storage & Serving

  • Uploaded plugin zips are extracted to data/render-plugins/<plugin-id>/.
  • All plugin files are served via Gitea’s internal static route, e.g. /assets/render-plugins/pdf-renderer/render.js.
  • Add a new DB table render_plugin to track installed plugins, their metadata, and enabled/disabled status.(Maybe we don't need a data table and can directly load the currently uploaded plugins dynamically?)

3. Admin UI

  • Add a new admin page listing all plugins (id, name, version, status).
  • Support upload (zip or git), enable/disable, and delete.
  • On upload, backend validates manifest/package.json, extracts files, and updates DB.

4. Frontend: Plugin Loading & Registration

  • On file view, frontend fetches all enabled plugin manifests.
  • For each file, match filePatterns and dynamically import the plugin’s render.js using ES module import.
  • Each plugin must export at least:
    • canHandle(filename, mimeType): boolean
    • render(container, fileUrl): Promise
  • Only call render() if canHandle() returns true.

5. Security

  • Only load plugins from local assets, never from external URLs.
  • Plugins run in a restricted context. If needed, can add iframe sandboxing later.
  • No access to Gitea internals or user data beyond what’s explicitly passed in.

6. Fallback

  • If no plugin matches, or plugin fails, fallback to the default file view.
  • Existing built-in renderers remain as fallback for now.
@kerwin612 commented on GitHub (Jul 2, 2025): Here’s a concrete plan for how this can be implemented in Gitea, step by step: **1. Plugin Packaging** - Each plugin is a zip archive (or git repo) containing either a manifest.json or package.json, plus a JS entry file (render.js) and optional assets. - The manifest file must include: - id / name (unique string) - version - filePatterns (array, e.g. [\"*.pdf\"]) OR MIME-TYPE??? - entry (relative path to main JS file) - description (optional) **2. Backend: Storage & Serving** - Uploaded plugin zips are extracted to `data/render-plugins/<plugin-id>/`. - All plugin files are served via Gitea’s internal static route, e.g. `/assets/render-plugins/pdf-renderer/render.js`. - Add a new DB table `render_plugin` to track installed plugins, their metadata, and enabled/disabled status.(Maybe we don't need a data table and can directly load the currently uploaded plugins dynamically?) **3. Admin UI** - Add a new admin page listing all plugins (id, name, version, status). - Support upload (zip or git), enable/disable, and delete. - On upload, backend validates manifest/package.json, extracts files, and updates DB. **4. Frontend: Plugin Loading & Registration** - On file view, frontend fetches all enabled plugin manifests. - For each file, match filePatterns and dynamically import the plugin’s render.js using ES module import. - Each plugin must export at least: - canHandle(filename, mimeType): boolean - render(container, fileUrl): Promise<void> - Only call render() if canHandle() returns true. **5. Security** - Only load plugins from local assets, never from external URLs. - Plugins run in a restricted context. If needed, can add iframe sandboxing later. - No access to Gitea internals or user data beyond what’s explicitly passed in. **6. Fallback** - If no plugin matches, or plugin fails, fallback to the default file view. - Existing built-in renderers remain as fallback for now.
Author
Owner

@silverwind commented on GitHub (Jul 2, 2025):

You can just use package.json only, the properties there are not strict, so you can easily add additional custom properties:

{
  "name": "myplugin",
  "version": "1.2.3",
  "gitea": {
    "type": "render-plugin",
    "filePatterns": ["*.foo"],
    "mimePatterns": ["text/foo"],
  },
}

There is not need for a entry because package.json already has that: https://nodejs.org/api/packages.html#package-entry-points. The spec for entry points is quite complicated, maybe initially only support "exports": "./index.js" syntax.

The benefit of being a valid npm package is that it can be published to npm registry for easy distribution. The whole zip file stuff could be skipped and plugins could be specified as npm package name only (downloaded on startup).

@silverwind commented on GitHub (Jul 2, 2025): You can just use `package.json` only, the properties there are not strict, so you can easily add additional custom properties: ```json { "name": "myplugin", "version": "1.2.3", "gitea": { "type": "render-plugin", "filePatterns": ["*.foo"], "mimePatterns": ["text/foo"], }, } ``` There is not need for a `entry` because package.json already has that: https://nodejs.org/api/packages.html#package-entry-points. The spec for entry points is quite complicated, maybe initially only support `"exports": "./index.js"` syntax. The benefit of being a valid npm package is that it can be published to npm registry for easy distribution. The whole zip file stuff could be skipped and plugins could be specified as npm package name only (downloaded on startup).
Author
Owner

@wxiaoguang commented on GitHub (Jul 2, 2025):

  • filePatterns (array, e.g. ["*.pdf"]) OR MIME-TYPE???

I do not think it is complete.

You should be able to answer the question by resolving "render a text-based STL 3D model correctly".

Each plugin must export at least:
* canHandle(filename, mimeType): boolean
* render(container, fileUrl): Promise

What if we need to change/re-design the plugin API in the future? https://github.com/go-gitea/gitea/issues/34917#issuecomment-3021670079 It needs to maintain the compatibility correctly.

@wxiaoguang commented on GitHub (Jul 2, 2025): > * filePatterns (array, e.g. ["*.pdf"]) OR MIME-TYPE??? I do not think it is complete. You should be able to answer the question by resolving "render a text-based STL 3D model correctly". > Each plugin must export at least: > * canHandle(filename, mimeType): boolean > * render(container, fileUrl): Promise What if we need to change/re-design the plugin API in the future? https://github.com/go-gitea/gitea/issues/34917#issuecomment-3021670079 **It needs to maintain the compatibility correctly.**
Author
Owner

@lunny commented on GitHub (Jul 6, 2025):

I think instead of manifest.json, we could leverage the existing package.json spec. The script itself should export a async function as its ESM default export that is imported and called by our code with likely a File blob to render and a destination DOM element to render to. opts for any additional options passed from gitea. Here's an example:

export default async function render(file: File, target: HTMLElement, opts: Record<string, any>): Promise {
target.innerHTML = "result";
}

We cannot scanning the repositories and get to know which one is a plugin or not if using the package.json?

@lunny commented on GitHub (Jul 6, 2025): > I think instead of `manifest.json`, we could leverage the existing `package.json` spec. The script itself should export a async function as its ESM default export that is imported and called by our code with likely a `File` blob to render and a destination DOM element to render to. `opts` for any additional options passed from gitea. Here's an example: > > export default async function render(file: File, target: HTMLElement, opts: Record<string, any>): Promise<void> { > target.innerHTML = "result"; > } We cannot scanning the repositories and get to know which one is a plugin or not if using the `package.json`?
Author
Owner

@silverwind commented on GitHub (Jul 7, 2025):

What do you mean by scanning?

@silverwind commented on GitHub (Jul 7, 2025): What do you mean by scanning?
Author
Owner

@kerwin612 commented on GitHub (Jul 8, 2025):

What do you mean by scanning?

When adding a plugin, you can directly add the Git repository address and then scan the source code files of the repository.

@kerwin612 commented on GitHub (Jul 8, 2025): > What do you mean by scanning? When adding a plugin, you can directly add the Git repository address and then scan the source code files of the repository.
Author
Owner

@silverwind commented on GitHub (Jul 8, 2025):

I don't follow. In above example you have gitea.type == "render-plugin" which defines it as a render plugin. Also, npm packages can be declared as git urls too: https://docs.npmjs.com/cli/v11/configuring-npm/package-json#git-urls-as-dependencies.

In any case, I'd strongly advice against defining a custom package format, just use the existing npm package format.

@silverwind commented on GitHub (Jul 8, 2025): I don't follow. In above example you have `gitea.type == "render-plugin"` which defines it as a render plugin. Also, npm packages can be declared as git urls too: https://docs.npmjs.com/cli/v11/configuring-npm/package-json#git-urls-as-dependencies. In any case, I'd strongly advice against defining a custom package format, just use the existing npm package format.
Author
Owner

@kerwin612 commented on GitHub (Jul 10, 2025):

What if we need to change/re-design the plugin API in the future? #34917 (comment) It needs to maintain the compatibility correctly.


1. Plugin Manifest & API Versioning

To ensure long-term compatibility and smooth evolution of the plugin system, I propose that both the Gitea core and each plugin explicitly declare their API version(s):

In the plugin package.json:

{
  "id": "pdf-renderer",
  "name": "PDF Renderer",
  "version": "1.0.0",
  "description": "Render PDF files directly in the Gitea file view.",
  "entry": "render.js",
  "filePatterns": ["*.pdf"],
  "apiVersion": "1.0.0"         // The plugin API version(s) this plugin supports
}

In the Gitea core:

  • The core will define its current plugin API version (e.g., "1.0.0").
  • When installing/enabling a plugin, Gitea will check for compatibility:
    • If the plugin’s apiVersion matches the core’s supported version(s), it can be enabled.
    • If not, the admin UI will display a clear warning and prevent activation.

Benefits:

  • This approach allows us to evolve the plugin API in the future (e.g., adding new hooks or changing signatures) without breaking existing plugins.
  • Plugin authors can update their plugins to support new API versions as needed.

2. Plugin Interface & API Evolution

Each plugin must export the interface required by the plugin API version it declares in its manifest. For example, with API version 1.0.0, the required exports might be:

// For API version 1.0.0
export function canHandle(filename: string, mimeType: string): boolean;
export function render(container: HTMLElement, fileUrl: string): Promise<void>;

If the plugin API evolves in the future (e.g., version 2.0.0 introduces new or changed methods), plugins targeting that version must export the updated interface as specified by the new API contract. This ensures that each plugin is always compatible with the core’s expectations for its declared API version.

Key points:

  • The core will validate that each plugin exports the correct interface for its declared apiVersion.
  • This approach allows us to introduce new features or make breaking changes in future API versions, while maintaining backward compatibility for existing plugins.
  • Plugin authors only need to update their code and manifest when they want to support a new API version.

@wxiaoguang What do you think of this design?

@kerwin612 commented on GitHub (Jul 10, 2025): > What if we need to change/re-design the plugin API in the future? [#34917 (comment)](https://github.com/go-gitea/gitea/issues/34917#issuecomment-3021670079) **It needs to maintain the compatibility correctly.** --- ## 1. **Plugin Manifest & API Versioning** To ensure long-term compatibility and smooth evolution of the plugin system, I propose that both the Gitea core and each plugin explicitly declare their API version(s): **In the plugin package.json:** ```json { "id": "pdf-renderer", "name": "PDF Renderer", "version": "1.0.0", "description": "Render PDF files directly in the Gitea file view.", "entry": "render.js", "filePatterns": ["*.pdf"], "apiVersion": "1.0.0" // The plugin API version(s) this plugin supports } ``` **In the Gitea core:** - The core will define its current plugin API version (e.g., `"1.0.0"`). - When installing/enabling a plugin, Gitea will check for compatibility: - If the plugin’s `apiVersion` matches the core’s supported version(s), it can be enabled. - If not, the admin UI will display a clear warning and prevent activation. **Benefits:** - This approach allows us to evolve the plugin API in the future (e.g., adding new hooks or changing signatures) without breaking existing plugins. - Plugin authors can update their plugins to support new API versions as needed. --- ## 2. **Plugin Interface & API Evolution** Each plugin must export the interface required by the plugin API version it declares in its manifest. For example, with API version `1.0.0`, the required exports might be: ```js // For API version 1.0.0 export function canHandle(filename: string, mimeType: string): boolean; export function render(container: HTMLElement, fileUrl: string): Promise<void>; ``` If the plugin API evolves in the future (e.g., version `2.0.0` introduces new or changed methods), plugins targeting that version must export the updated interface as specified by the new API contract. This ensures that each plugin is always compatible with the core’s expectations for its declared API version. **Key points:** - The core will validate that each plugin exports the correct interface for its declared `apiVersion`. - This approach allows us to introduce new features or make breaking changes in future API versions, while maintaining backward compatibility for existing plugins. - Plugin authors only need to update their code and manifest when they want to support a new API version. --- @wxiaoguang What do you think of this design?
Author
Owner

@wxiaoguang commented on GitHub (Jul 10, 2025):

Better than before, I think there are still unresolved concerns https://github.com/go-gitea/gitea/issues/34917#issuecomment-3027624778 :

I do not think it is complete.
You should be able to answer the question by resolving "render a text-based STL 3D model correctly".

@wxiaoguang commented on GitHub (Jul 10, 2025): Better than before, I think there are still unresolved concerns https://github.com/go-gitea/gitea/issues/34917#issuecomment-3027624778 : > I do not think it is complete. > You should be able to answer the question by resolving "render a text-based STL 3D model correctly".
Author
Owner

@kerwin612 commented on GitHub (Jul 10, 2025):

Do you think this design is feasible?:

1. Flexible canHandle Interface

The canHandle function can optionally receive a readChunk callback, allowing the plugin to read a small portion of the file content for more accurate detection:

// Example for API version 1.1.0+
export async function canHandle(options: {
  filename: string,
  mimeType: string,
  readChunk: (start: number, length: number) => Promise<Uint8Array>
}): Promise<boolean> {
  // Example: Read first 1KB to check for ASCII STL signature
  const chunk = await options.readChunk(0, 1024);
  // ...analyze chunk...
  return isAsciiSTL(chunk) || isBinarySTL(chunk);
}
  • This approach allows plugins to intelligently detect file types based on actual content, not just metadata.
  • For performance, plugins should only use readChunk when necessary.

2. Backward Compatibility

  • For plugins that don’t need content inspection, the API remains simple: they can just use filename and mimeType as before.

3. Manifest Declaration

  • Plugins can declare in their manifest if they require content-based detection, so the core can optimize file access accordingly.

This design ensures:

  • Accurate handling of complex file types (like text-based STL 3D models)
  • Backward compatibility and performance
  • Maximum flexibility for plugin authors
@kerwin612 commented on GitHub (Jul 10, 2025): **Do you think this design is feasible?:** #### 1. **Flexible canHandle Interface** The `canHandle` function can optionally receive a `readChunk` callback, allowing the plugin to read a small portion of the file content for more accurate detection: ```js // Example for API version 1.1.0+ export async function canHandle(options: { filename: string, mimeType: string, readChunk: (start: number, length: number) => Promise<Uint8Array> }): Promise<boolean> { // Example: Read first 1KB to check for ASCII STL signature const chunk = await options.readChunk(0, 1024); // ...analyze chunk... return isAsciiSTL(chunk) || isBinarySTL(chunk); } ``` - This approach allows plugins to intelligently detect file types based on actual content, not just metadata. - For performance, plugins should only use `readChunk` when necessary. #### 2. **Backward Compatibility** - For plugins that don’t need content inspection, the API remains simple: they can just use `filename` and `mimeType` as before. #### 3. **Manifest Declaration** - Plugins can declare in their manifest if they require content-based detection, so the core can optimize file access accordingly. --- **This design ensures:** - Accurate handling of complex file types (like text-based STL 3D models) - Backward compatibility and performance - Maximum flexibility for plugin authors
Author
Owner

@wxiaoguang commented on GitHub (Jul 10, 2025):

Backend already pre-fetched 1024 bytes from that file

@wxiaoguang commented on GitHub (Jul 10, 2025): Backend already pre-fetched 1024 bytes from that file
Author
Owner

@hiifong commented on GitHub (Jul 11, 2025):

I'm wondering if we should go for pluginization through the backend so that we can have more flexible operations, the backend we can handle the corresponding js, but a pure js implementation of a plugin doesn't provide the backend functionality. If we implement the plugin through the backend maybe we can use https://github.com/hashicorp/go-plugin.

@hiifong commented on GitHub (Jul 11, 2025): I'm wondering if we should go for pluginization through the backend so that we can have more flexible operations, the backend we can handle the corresponding js, but a pure js implementation of a plugin doesn't provide the backend functionality. If we implement the plugin through the backend maybe we can use https://github.com/hashicorp/go-plugin.
Author
Owner

@wxiaoguang commented on GitHub (Jul 11, 2025):

I'm wondering if we should go for pluginization through the backend so that we can have more flexible operations, the backend we can handle the corresponding js, but a pure js implementation of a plugin doesn't provide the backend functionality. If we implement the plugin through the backend maybe we can use https://github.com/hashicorp/go-plugin.

I don't see who has the ability&time to implement it. And ROI (return on investment) is very slow.

@wxiaoguang commented on GitHub (Jul 11, 2025): > I'm wondering if we should go for pluginization through the backend so that we can have more flexible operations, the backend we can handle the corresponding js, but a pure js implementation of a plugin doesn't provide the backend functionality. If we implement the plugin through the backend maybe we can use https://github.com/hashicorp/go-plugin. * https://github.com/go-gitea/gitea/issues/20126#issuecomment-2716442774 I don't see who has the ability&time to implement it. And ROI (return on investment) is very slow.
Author
Owner

@kerwin612 commented on GitHub (Jul 15, 2025):

@wxiaoguang after the current discussion, do you have any other suggestions? I think we can first come up with a draft and start working on it, right? Only by taking action can we continuously improve it!

@kerwin612 commented on GitHub (Jul 15, 2025): @wxiaoguang after the current discussion, do you have any other suggestions? I think we can first come up with a draft and start working on it, right? Only by taking action can we continuously improve it!
Author
Owner

@wxiaoguang commented on GitHub (Jul 15, 2025):

  1. Should not use package.json.
    • It is definitely an abuse and will cause more problems.
  2. Need to define some rules (e.g. regexp) to make backend able to detect which render can handle the file
    • That's why should not use package.json
    • Frontend code should not make every plugin read the raw file again and again
  3. Refactor the existing renders like asciicast and console first
@wxiaoguang commented on GitHub (Jul 15, 2025): 1. Should not use `package.json`. * It is definitely an abuse and will cause more problems. 2. Need to define some rules (e.g. regexp) to make backend able to detect which render can handle the file * That's why should not use package.json * Frontend code should not make every plugin read the raw file again and again 3. Refactor the existing renders like `asciicast` and `console` first
Author
Owner

@silverwind commented on GitHub (Jul 15, 2025):

should not use package.json

Why not use existing package infrastructure? The benefit is plugins could be published to the npm registry and directly used from there by only specifying package name. I'd certainly prefer to install a plugin with a simple command like gitea plugin add <packagename> and not having to mess with any files.

@silverwind commented on GitHub (Jul 15, 2025): > should not use package.json Why not use existing package infrastructure? The benefit is plugins could be published to the npm registry and directly used from there by only specifying package name. I'd certainly prefer to install a plugin with a simple command like `gitea plugin add <packagename>` and not having to mess with any files.
Author
Owner

@wxiaoguang commented on GitHub (Jul 15, 2025):

Well, every time I say "it would cause problems", then it really causes problems.

@wxiaoguang commented on GitHub (Jul 15, 2025): Well, every time I say "it would cause problems", then it really causes problems.
Author
Owner

@silverwind commented on GitHub (Jul 15, 2025):

Why you want to redesign a JS package system is beyond me.

@silverwind commented on GitHub (Jul 15, 2025): Why you want to redesign a JS package system is beyond me.
Author
Owner

@wxiaoguang commented on GitHub (Jul 15, 2025):

Why you want to redesign a JS package system is beyond me.

Why you want to abuse the JS package system to support Gitea frontend plugin? For example:

  • How to support the customized fields without corrupting or conflicting the package.json definition?
  • Do you want to make gitea plugin add <packagename> behave like npm install and handle various dependencies?
  • Do you want to re-implement the package.json version comparing logic in Gitea's Golang code?
@wxiaoguang commented on GitHub (Jul 15, 2025): > Why you want to redesign a JS package system is beyond me. Why you want to abuse the JS package system to support Gitea frontend plugin? For example: * How to support the customized fields without corrupting or conflicting the `package.json` definition? * Do you want to make `gitea plugin add <packagename>` behave like `npm install` and handle various dependencies? * Do you want to re-implement the `package.json` version comparing logic in Gitea's Golang code?
Author
Owner

@wxiaoguang commented on GitHub (Jul 15, 2025):

The benefit of being a valid npm package is that it can be published to npm registry for easy distribution.

These published npm packages are Gitea-only, no JS eco-system user can use them (these packages just don't work for non-Gitea environments). Isn't it an abuse to the npm package services and the JS eco-system?

@wxiaoguang commented on GitHub (Jul 15, 2025): > The benefit of being a valid npm package is that it can be published to npm registry for easy distribution. These published npm packages are Gitea-only, no JS eco-system user can use them (these packages just don't work for non-Gitea environments). Isn't it an abuse to the npm package services and the JS eco-system?
Author
Owner

@silverwind commented on GitHub (Jul 15, 2025):

How to support the customized fields without corrupting or conflicting the package.json definition?

package.json spec allows any additional root level properties and this is already in use by numerous other tools.

Do you want to make gitea plugin add behave like npm install and handle various dependencies?

Ideally yes. And yes I realize this brings a dependency on npm.

Do you want to re-implement the package.json version comparing logic in Gitea's Golang code?

It's just semver for which I'm sure a golang implementation exists. Edit: https://github.com/Masterminds/semver.

@silverwind commented on GitHub (Jul 15, 2025): > How to support the customized fields without corrupting or conflicting the package.json definition? package.json spec allows any additional root level properties and this is already in use by numerous other tools. > Do you want to make gitea plugin add <packagename> behave like npm install and handle various dependencies? Ideally yes. And yes I realize this brings a dependency on `npm`. > Do you want to re-implement the package.json version comparing logic in Gitea's Golang code? It's just semver for which I'm sure a golang implementation exists. Edit: https://github.com/Masterminds/semver.
Author
Owner

@silverwind commented on GitHub (Jul 15, 2025):

These published npm packages are Gitea-only, no JS eco-system user can use them (these packages just don't work for non-Gitea environments). Isn't it an abuse to the npm package services and the JS eco-system?

The entry file could be used standalone in a HTML file to test. See for example this. This is a npm package and I'm directly importing the entry file in HTML for preview purposes.

@silverwind commented on GitHub (Jul 15, 2025): > These published npm packages are Gitea-only, no JS eco-system user can use them (these packages just don't work for non-Gitea environments). Isn't it an abuse to the npm package services and the JS eco-system? The entry file could be used standalone in a HTML file to test. See for example [this](https://github.com/silverwind/clippie/blob/c4bd5ea9b73a84e123eb81ad75eb1603f232c96a/index.html#L11). This is a npm package and I'm directly importing the entry file in HTML for preview purposes.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/gitea#14685