主流編輯器整合
SoFinder 將 sofinder-picker.js 作為獨立 ES Module 發布。所有編輯器使用同一個 Picker URL 與帶版本的回傳物件,不依賴 React 內部實作。
回傳物件遵循公開的 Picker Entry JSON Schema,固定包含 resource、path、name、url、mimeType、size、modifiedAt、width、height 與 capabilities。非圖片尺寸為 null;Consumer 必須忽略 1.x 新增欄位。訊息 Envelope 另見 JSON Schema。
上傳回應與新版 Picker 也會回傳相容新增的 Asset Reference 1.0:包含版本指紋、下載 URL、可選穩定資產 ID、替代文字、響應式變體與 embeddable 能力;原有 entry 不會移除。
import { openPicker } from '/sofinder/assets/sofinder-picker.js'
const entry = await openPicker({
baseUrl: '/sofinder/browser',
kind: 'image',
resource: 'Images',
path: 'articles/2026'
})SDK 使用彈窗回傳結果,並同時驗證彈窗物件、Origin、協議版本及隨機 Request ID; 不使用萬用字元 postMessage。Host 與 Picker 必須同源,伺服器仍會執行登入、ACL 及圖片格式驗證。
傳入 resource 時預設會鎖定 Picker、隱藏其他資源並拒絕資源不符的結果。 若它只應作為初始位置,請傳入 lockResource: false;直接建立 Picker URL 時 使用 resourceLock=0。
SoEditor
SoEditor 負責 HTML 內容編輯,SoFinder 負責素材管理。透過官方 @soeditor/adapter-sofinder 配接器,可以選擇圖片、附件、影片檔案與封面。
安裝相同版本的 @soeditor/editor、@soeditor/presets、@soeditor/file-manager 與 @soeditor/adapter-sofinder(本例適用於 1.4.0)。頁面加入 <textarea id="content"></textarea>,再將以下模組放入應用程式建置:
import { createClassicEditor as createOptionalEditor } from '@soeditor/editor/cms/optional'
import '@soeditor/editor/cms/styles.css'
import { cmsRuntimePreset } from '@soeditor/presets/cms-runtime'
import { FileManagerPlugin, fileManagerServiceToken } from '@soeditor/file-manager'
import { SoFinderAdapter } from '@soeditor/adapter-sofinder'
import { openPicker } from '/sofinder/assets/sofinder-picker.js'
const editor = await createOptionalEditor(document.querySelector('#content'), {
plugins: [...cmsRuntimePreset.plugins, FileManagerPlugin],
toolbar: ['undo', 'redo', 'bold', 'italic', 'image-browse', 'file-link', 'cmsVideo'],
})
editor.editor.services.register(fileManagerServiceToken, new SoFinderAdapter({
async pick({ kind, accept }) {
try {
const entry = await openPicker({
baseUrl: '/sofinder/browser',
kind: kind === 'image' ? 'image' : 'file',
resource: kind === 'image' ? 'Images' : 'Files',
})
const mime = entry.mimeType?.toLowerCase() ?? ''
if (accept?.length && !accept.some(type => type.endsWith('/*')
? mime.startsWith(type.slice(0, -1).toLowerCase())
: mime === type.toLowerCase())) {
throw new Error('The selected file does not match the requested format.')
}
if (entry.capabilities.embeddable === false) {
throw new Error('The selected file cannot be embedded.')
}
return {
url: entry.url,
name: entry.name,
...(entry.mimeType != null ? { mimeType: entry.mimeType } : {}),
...(entry.assetId != null ? { assetId: entry.assetId } : {}),
...(entry.alt != null ? { alt: entry.alt } : {}),
...(entry.width != null ? { width: entry.width } : {}),
...(entry.height != null ? { height: entry.height } : {}),
}
} catch (error) {
if (error instanceof DOMException && error.name === 'AbortError') return null
throw error
}
},
}))將瀏覽器、靜態資源路由及 Images / Files 替換為實際部署設定。media 請求使用檔案選擇器,橋接函式在回傳前驗證要求的 MIME 類型。關閉視窗只取消選擇,不插入內容;其他錯誤繼續回報。空尺寸會省略,明確的空替代文字會保留。明確設定外掛清單時須保留 cmsRuntimePreset.plugins。
本例選擇既有素材。貼上、拖放直接上傳需要另行設定 SoFinderUploadAdapter 與宿主上傳 SDK。登入、資源權限與穩定的存取 URL 仍由實際部署負責。編輯器端說明請見 SoEditor 整合指南。
CKEditor 5
在應用程式正常安裝 CKEditor 5 及 Image Plugin,再將編輯器實例交給 SoFinder:
import { selectForCkeditor5 } from '/sofinder/assets/sofinder-picker.js'
button.addEventListener('click', () => selectForCkeditor5(editor, {
baseUrl: '/sofinder/browser',
resource: 'Images',
language: 'zh-tw'
}))Adapter 呼叫 CKEditor 5 公開的 insertImage 與替代文字 Command。啟用資產目錄後,Picker 與上傳會保留明確的 alt="",先依 language/locale 讀取 altTranslations,再使用資產預設替代文字;仍未設定時才使用移除副檔名的檔名。語言鍵採規範化 BCP 47 風格,例如 en、zh-tw、fr-ca,中繼資料 API 最多接受 20 種語言;上傳 Adapter 可傳 locale: 'zh-tw'。同時會盡量寫入響應式變體、寬高及 data-sofinder-asset-id。安裝與授權要求請參考 CKEditor 自行託管官方指南。 既有 CKEditor 4 Callback 與 Quick Upload Endpoint 會繼續保留。
本機選擇、貼上及桌面拖入使用獨立官方 Upload Adapter,不會把 CKEditor 打入 SoFinder 主套件:
import { createCkeditor5UploadPlugin } from '/sofinder/assets/sofinder-ckeditor5.js'
ClassicEditor.create(element, {
extraPlugins: [createCkeditor5UploadPlugin({ apiBase: '/sofinder/api', csrfToken, resource: 'Images' })]
})它與 TinyMCE、TipTap、Quill、Markdown 和一般表單入口共用 sofinder-sdk.js 上傳任務,統一處理進度、取消、重試、分塊恢復及同名檔案選擇。Private 資源沒有穩定鑑權交付 URL 時會拒絕永久插入,臨時簽章 URL 不會被視為可嵌入位址。Factory 回傳的是可建構 CKEditor Plugin,請勿手動呼叫或再包一層 Plugin 類別。
TinyMCE
在 tinymce.init() 前註冊 Plugin:
import { registerTinyMce } from '/sofinder/assets/sofinder-picker.js'
registerTinyMce(tinymce, {
baseUrl: '/sofinder/browser',
resource: 'Images'
})
tinymce.init({
selector: '#editor',
plugins: 'sofinder link image',
toolbar: 'undo redo | bold italic | image sofinder'
})Adapter 透過 TinyMCE 公開 API 插入已編碼的 <img>。CDN Key 與自託管方式參見 TinyMCE 官方部署文件。
直接上傳請從 sofinder-tinymce.js 使用 createTinyMceUploadIntegration(editor, options),並在 TinyMCE 的 setup Callback 建立。它會在圖片節點產生後補齊 alt、寬高、srcset 和穩定資產 ID,而不只保留上傳 URL。
TipTap 與 Quill
TipTap 安裝 Image Extension 後呼叫:
await selectForTiptap(editor, { baseUrl: '/sofinder/browser', resource: 'Images' })Quill 在初始化含圖片按鈕的 Toolbar 後呼叫:
registerQuill(quill, { baseUrl: '/sofinder/browser', resource: 'Images' })wangEditor 5
外部選擇按鈕使用公開節點 API,本機選擇、貼上和拖入則使用 MENU_CONF.uploadImage.customUpload:
import { selectForWangEditor } from '/sofinder/assets/sofinder-picker.js'
import { createWangEditorUploadIntegration } from '/sofinder/assets/sofinder-wangeditor.js'
const editorConfig = {
MENU_CONF: {
uploadImage: createWangEditorUploadIntegration({
apiBase: '/sofinder/api', csrfToken, resource: 'Images'
})
}
}
button.addEventListener('click', () => selectForWangEditor(editor, {
baseUrl: '/sofinder/browser', resource: 'Images', language: 'zh-tw'
}))若要讓 wangEditor 內建圖片按鈕直接開啟 SoFinder,可把 createWangEditorPickerIntegration(options) 設定到 MENU_CONF.uploadImage。 Adapter 遵循 wangEditor 5 公開的 insertNode、customUpload 與 customBrowseAndUpload 介面,不會把編輯器 Runtime 打入 SoFinder。
Jodit 4
將上傳設定交給 Jodit,並以外部按鈕開啟素材選擇器:
import { selectForJodit } from '/sofinder/assets/sofinder-picker.js'
import { createJoditUploadIntegration } from '/sofinder/assets/sofinder-jodit.js'
const editor = Jodit.make('#editor', {
uploader: createJoditUploadIntegration({
apiBase: '/sofinder/api', csrfToken, resource: 'Images'
})
})
button.addEventListener('click', () => selectForJodit(editor, {
baseUrl: '/sofinder/browser', resource: 'Images', language: 'zh-tw'
}))Jodit 內建圖片對話框、貼上及拖入會使用公開的自訂上傳介面;Picker 與上傳均透過 createInside.element() 和 s.insertImage() 插入完整資產屬性,不會將 Jodit 打入 SoFinder 套件。
一般表單與 Markdown
一般輸入框使用 selectForInput(input, options);它會寫入 URL 並觸發可冒泡的 input 與 change 事件。文件使用 kind: 'file',網頁圖片使用 kind: 'image'。
專案內本機示範
啟動 examples/symfony,以 demo / demo 登入後開啟 /integrations。頁面會用 本機 SoFinder 後端及 Picker SDK 實際連接 CKEditor 5、TinyMCE 8、TipTap、Quill 2、 wangEditor 5、Jodit 4 和一般表單。第三方編輯器從其文件列出的 CDN 載入;部署前必須檢查各編輯器授權並 替換示範 Key。
整合示範固定所有第三方 CDN 版本。每週按需執行的 Editor network compatibility 工作流程會在 Chromium 開啟頁面並等待真實 CKEditor、TinyMCE、TipTap、Quill、wangEditor 與 Jodit 實例;僅下載 Script 或出現佔位 DOM 不算初始化成功。
目錄深層連結可以直接收藏:
/sofinder/browser?uiMode=manager&type=Images&path=articles/2026瀏覽器導覽時會同步 type 與 path;此處保存邏輯路徑,不會暴露伺服器檔案系統路徑。