mirror of
https://github.com/mudler/LocalAI.git
synced 2026-10-28 09:08:54 -04:00
feat(addons): ship a committed icon.png instead of the generated square
The shared UI builder and the Tables builder copy ui/icon.png from the add-on source tree to the package unchanged when the file exists. They write the generated placeholder only when it does not. Add a fixture with an icon and a test for both cases. Assisted-by: Claude Code:claude-sonnet-5-5 node Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
This commit is contained in:
6 files changed
+23
-3
No files matched your search
Binary file not shown.
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,4 @@
|
||||
export function createAddon(sdk) {
|
||||
const h = sdk.React.createElement
|
||||
return { Component: () => <p className="fx">fixture</p>, dispose() {} }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
.fx { color: inherit; }
|
||||
@@ -30,6 +30,14 @@ test('buildAddonUI writes the module, the style sheet and a PNG icon', async ()
|
||||
assert.deepEqual(await firstPixel(png), [26, 62, 138])
|
||||
})
|
||||
|
||||
test('a committed icon.png next to src is copied unchanged and no square is generated', async () => {
|
||||
const root = join(here, 'fixture-icon')
|
||||
const dist = await buildAddonUI({ root, name: 'fixture', rgb: [26, 62, 138] })
|
||||
const out = readFileSync(join(dist, 'ui/icon.png'))
|
||||
assert.ok(out.equals(readFileSync(join(root, 'icon.png'))), 'the packaged icon is the committed file')
|
||||
assert.equal(out.readUInt32BE(16), 256, 'the committed icon keeps its own size, not the 128 pixel square')
|
||||
})
|
||||
|
||||
test('the bundle is an ES module that exports createAddon and calls h for JSX', async () => {
|
||||
const dist = await buildAddonUI({ root: join(here, 'fixture'), name: 'fixture', rgb: [1, 2, 3] })
|
||||
const js = readFileSync(join(dist, 'ui/fixture.js'), 'utf8')
|
||||
|
||||
@@ -136,7 +136,11 @@ export async function buildAddonUI({ root, name, rgb, compat }) {
|
||||
} else {
|
||||
copyFileSync(join(root, 'src/style.css'), join(out, `ui/${name}.css`))
|
||||
}
|
||||
writeFileSync(join(out, 'ui/icon.png'), solidIcon(rgb))
|
||||
// A committed icon (ui/icon.png next to src/) ships unchanged. Only an
|
||||
// add-on without one gets the generated solid square.
|
||||
const icon = join(root, 'icon.png')
|
||||
if (existsSync(icon)) copyFileSync(icon, join(out, 'ui/icon.png'))
|
||||
else writeFileSync(join(out, 'ui/icon.png'), solidIcon(rgb))
|
||||
const js = readFileSync(join(out, `ui/${name}.js`), 'utf8')
|
||||
const bad = reactImport.exec(js)
|
||||
if (bad) throw new Error(`the bundle must not import React: found ${bad[0]}`)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import { createRequire } from 'node:module'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
import { dirname, join, resolve } from 'node:path'
|
||||
import { mkdirSync, rmSync, writeFileSync, copyFileSync, readFileSync } from 'node:fs'
|
||||
import { existsSync, mkdirSync, rmSync, writeFileSync, copyFileSync, readFileSync } from 'node:fs'
|
||||
import { deflateSync } from 'node:zlib'
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url))
|
||||
@@ -78,7 +78,10 @@ export async function build() {
|
||||
logLevel: 'warn',
|
||||
})
|
||||
copyFileSync(join(here, 'src/style.css'), join(out, 'ui/tables.css'))
|
||||
writeFileSync(join(out, 'ui/icon.png'), icon())
|
||||
// A committed ui/icon.png ships unchanged; the drawn grid is the fallback.
|
||||
const committed = join(here, 'icon.png')
|
||||
if (existsSync(committed)) copyFileSync(committed, join(out, 'ui/icon.png'))
|
||||
else writeFileSync(join(out, 'ui/icon.png'), icon())
|
||||
const js = readFileSync(join(out, 'ui/tables.js'), 'utf8')
|
||||
if (/from\s+['"](react|react-dom)/.test(js) || /require\(/.test(js)) throw new Error('the bundle must not import React')
|
||||
return out
|
||||
|
||||
Reference in new issue
Block a user