Pollinations React 参考应用:三层 UI 体系与 SDK 集成的前端开发指南 AI 应用媒体生成后端前端【免费下载链接】pollinationsYour Friendly Open-Source Gen-AI Platform项目地址https://gitcode.com/gh_mirrors/po/pollinations点击查看免费下载react.pollinations.ai仓库中的 apps/react是 Pollinations 官方的 React 参考应用也是pollinations/ui组件库与pollinations/sdk认证/数据钩子的“活体文档”。读完本篇你会掌握该应用 Primitives / Compositions / Modules 三层 UI 的边界与用法、PolliProvider的 PKCE 登录流程、useModelCatalog等 SDK 钩子如何驱动真实账号与模型目录数据以及完整的本地开发与部署链路。应用总览一个展示三层 UI 的公共参考应用根据 apps/react/README.md 的描述该应用是一个 Public showcase for React UI surfaces公共界面围绕三个层级组织Primitives—— 从packages/ui/src/primitives导出的最小构建块按钮、输入框、Chip、文本等单一职责元素不含任何应用逻辑Compositions—— 从packages/ui/src/compositions导出的免 SDK 配方复制按钮、文件上传器、时间窗口选择器等自带状态与行为但仍不需要接入账号体系Modules—— 从packages/ui/src/modules导出的 SDK 支撑模块流认证、模型选择、钱包等真实业务功能。从源码看实际上还有第四个公共视图Colorscolors-page.tsx用于展示主题色彩 token 体系后文单独说明。README 还特别提到一点穷举式 primitive 覆盖页仍可通过?viewshowcase访问用于内部视觉 QA但故意不链接到公共 UI。这一点在 App.tsx 中可以直接印证——AppView类型是PublicAppView | showcase的联合而渲染顶部 Tab 的PUBLIC_VIEWS数组只包含primitives、compositions、modules、colors四项showcase只能通过 URL 参数直接访问。该页面由 DesignShowcase.tsx 实现其源码注释也解释了原因// App-owned design primitive showcase. // This demo composes public pollinations/ui exports only; it is deliberately // kept outside the package so the published library stays primitive/module-only.也就是说展示页刻意留在应用侧而非packages/ui内保证发布的组件库只包含 primitive/composition/module 三类导出职责边界非常清晰。视图路由URL 查询参数驱动的状态整个应用没有引入路由库而是用?view查询参数实现视图切换。核心逻辑在 App.tsx 的readAppView()与useAppView()中readAppView()从window.location.search读取view参数合法值校验为primitives | compositions | modules | colors | showcase旧值models、react会重定向到modules说明该应用经历过视图命名迭代做了向后兼容selectView()通过history.pushState写回 URL选择primitives时会url.searchParams.delete(view)作为默认视图不写参数其余值则set(view, view)同时清空 hash监听popstate事件保证浏览器前进/后退时视图同步。// 摘自 apps/react/src/App.tsx const PUBLIC_VIEWS: { id: PublicAppView; label: string }[] [ { id: primitives, label: Primitives }, { id: compositions, label: Compositions }, { id: modules, label: Modules }, { id: colors, label: Colors }, ]; function useAppView() { const [activeView, setActiveView] useStateAppView(readAppView); useEffect(() { const handlePopState () setActiveView(readAppView()); window.addEventListener(popstate, handlePopState); return () window.removeEventListener(popstate, handlePopState); }, []); // ... }外壳布局AppShell由ScrollArea提供纵向滚动AppHeader来自pollinations/ui内部渲染TabButton切换 Tab 和ColorModeToggle明暗切换autoHidescrollTargetRef配合实现滚动时自动隐藏头部。开发环境脚本、依赖与 Vite 配置npm 脚本与 monorepo 构建顺序package.json 定义了如下脚本scripts: { dev: vite, predev: npm run build --prefix ../../packages/sdk npm run build --prefix ../../packages/ui, build: vite build, prebuild: npm run build --prefix ../../packages/sdk npm run build --prefix ../../packages/ui, preview: vite preview, pretypecheck: npm run build --prefix ../../packages/sdk npm run build --prefix ../../packages/ui, typecheck: tsc --noEmit }关键点predev/prebuild/pretypecheck三个前置钩子都会先构建../../packages/sdk和../../packages/ui两个工作区包再执行 Vite 或 tsc。这符合 README 中Run fromapps/react的说法——日常开发只需两条命令npm run dev npm run typecheck依赖方面应用依赖react/react-dom^19.2.0工作区包pollinations/sdk与pollinations/ui均为*版本devDependencies 为 Vite 7、TypeScript 5.7、Tailwind CSS 4tailwindcss/vite与vitejs/plugin-react。为什么需要resolve.dedupevite.config.ts 只有十几行但其中一条配置值得所有 monorepo 前端开发者注意export default defineConfig({ plugins: [react(), tailwindcss()], // Monorepo has React 18 in apps/react packages/sdk packages/ui and // React 19 at the workspace root. Without dedupe, the symlinked workspace // packages can resolve a different React copy than the app, producing // Invalid hook call or a split React context. resolve: { dedupe: [react, react-dom], }, });源码注释解释了原因monorepo 内不同工作区可能解析到不同的 React 副本React 18 与 19 并存于根工作区与子包符号链接的 workspace 包若解析到另一份 React会产生 Invalid hook call 或 React context 分裂。dedupe: [react, react-dom]强制统一为同一实例是这类问题的标准解法。入口 main.tsx 是标准写法引入app.css在StrictMode下createRoot(document.getElementById(root)).render(App /)并在缺少#root元素时直接抛错。App Key 与 SDK 配置config.ts 里的两个常量apps/react/src/config.ts 是全应用最重要的配置文件只有两个导出// Publishable key for this showcase (pk_* is safe to commit). // Created via polli keys create --type publishable with redirect URIs // http://localhost:5173 and https://react.pollinations.ai. export const APP_KEY pk_kZRl8saq8s2h9ome; // Point the catalog at a local gen worker in dev (VITE_GEN_BASE_URLhttp://localhost:8788). // Unset falls back to the SDK default (production gen.pollinations.ai). export const GEN_BASE_URL import.meta.env.VITE_GEN_BASE_URL || undefined;两个配置各自说明了该应用与 Pollinations 平台的两个关键集成面APP_KEYpublishable keypk_*前缀它是应用标识符而不是用户的 API key因此可以安全地提交到代码库。注释中给出了完整的创建方式polli keys create --type publishable并需要登记两个 redirect URI——本地开发用的http://localhost:5173Vite 默认端口和生产的https://react.pollinations.ai。这个 key 最终会传给PolliProvider appKey{APP_KEY}。GEN_BASE_URL通过 Vite 环境变量VITE_GEN_BASE_URL可选地把模型目录请求指向本地 gen workerhttp://localhost:8788不设置时回退到 SDK 默认的gen.pollinations.ai生产地址。这为本地联调 gen 后端提供了零代码改动的开关。PolliProvider 的完整能力PolliProvider是 packages/sdk/src/react/PolliProvider.tsx 导出的认证上下文容器。本应用使用了其中最基本的两个属性PolliProvider appKey{APP_KEY} permissions{[profile]} AppShell activeView{activeView} onSelectView{selectView} / /PolliProvider而它的完整 props 面见PolliProviderPropsPolliProvider.tsx#L29-L53还包括属性默认值说明appKey必填Publishable keypk_...是应用标识而非用户 API keystoragelocalStorage保存 API key 与 OAuth 状态也接受sessionStorage或自定义同步 adapterpermissions[]登录时请求的 OAuth scope本应用为[profile]models省略允许使用的模型白名单省略或[]表示全部login({ models })可覆盖budget省略该 key 的 Pollen 预算login({ budget })可覆盖expiry省略key 有效天数login({ expiry })可覆盖enterUrlhttps://enter.pollinations.ai授权服务地址apiBaseUrlenterUrl /api账号 API 地址从源码实现看登录走的是标准Authorization Code PKCE流程login()生成随机state与 PKCE verifier两段crypto.randomUUID去连字符拼接challenge 用crypto.subtle.digest(SHA-256)计算然后重定向到${enterUrl}/authorize?client_id...response_typecodecode_challenge_methodS256...见buildAuthorizeUrlPolliProvider.tsx#L55-L86挂载时的一次性 hydration effect 通过consumeOAuthCallback处理回跳 URL校验state取出code与 verifierPOST 到${enterUrl}/api/oauth/token换取access_tokenexchangeAuthorizationCodePolliProvider.tsx#L116-L145token 存入polli:{appKey}:token键下源码注释特别提到hydrationStarted这个 ref 的存在原因StrictMode 会重放 effects而授权码只能用一次所以必须用 ref 防止二次消费——这也解释了为什么main.tsx要开StrictMode的同时 Provider 内部要做防护。PolliProvider.test.tsxpackages/sdk/src/react/PolliProvider.test.tsx中有对应的用例pk_前缀校验警告warns when appKey is not a publishable key、state/verifier 写入 localStorage、token 存取与logout清理等。此外源码中warnAuthSetup()会在非生产环境打印提示若appKey不以pk_开头会警告去 Enter 创建若 redirect URI 是 loopback HTTP 地址会提示记得把它加进 App Key 的 redirectUris——这正是config.ts注释里登记http://localhost:5173的原因。Modules 视图SDK 钩子驱动的账号与模型目录?viewmodules页面modules-page.tsx是三层中唯一接真实数据的一层开头文案定义很准确Modules are domain features wired to the SDK and live data — authentication, model selection, wallet。用到的 SDK 钩子页面顶部一次性拉取四个数据源modules-page.tsx#L233-L244const { models, allowedModelIds, isLoggedIn, isLoading, error } useModelCatalog({ baseUrl: GEN_BASE_URL }); const profile useAccountProfile({ enabled: isLoggedIn }); const accountKey useAccountKey({ enabled: isLoggedIn }); const keyUsage useAccountKeyUsage({ enabled: isLoggedIn, days: 30, limit: 1 });这些钩子由pollinations/sdk/react导出入口见 packages/sdk/src/react/index.ts底层实现在 hooks.ts可以确认几个行为细节所有 hook 必须在PolliProvider内使用否则useRequiredAuth()直接抛错Pollinations auth hooks must be used within aPolliProvider账号类 hook 都接受enabled参数本应用传{ enabled: isLoggedIn }即游客状态不发请求登录后自动拉取useAccountKeyUsage的days: 30, limit: 1会拼装成查询参数请求/account/key/usage?days30limit1hooks.ts#L263-L293useModelCatalog返回models全量目录与allowedModelIds当前 key 允许的模型集合登录态下二者结合用于过滤可见模型从源码看所有账号请求收到401时会自动调用logout()useAccountResource内if (err.status 401) logout()hooks.ts#L134-L137token 失效时 UI 会自然回到未登录态。页面结构与访问权限逻辑页面分两个Surface variantpanel区块Auth 区渲染AppUserMenu来自pollinations/ui/app-user-menu/sdk子路径导出最终实现是 AppUserMenu.tsx未登录时显示红色提示 Authorize the app to load your Pollinations account and per-key access。下方两张表分别是 AccountUserAvatar头像、GitHub Username/Name、Email来自useAccountProfile与 App access per keypollenBudget、expiresAt、30 天用量、分类 chips、固定的 20% of pollen spent in-app 收益 chip来自useAccountKey与useAccountKeyUsage。Gen 区按CATEGORY_ORDERimage, video, text, audio, embedding, realtime过滤出有可用模型的分类渲染ModalityTab分类切换和ModelSelector均来自pollinations/ui/gen导出清单见 packages/ui/src/modules/gen/index.ts。选中模型后展示 ID、Publisher、Access、Description、输入/输出 modality、Tools/Reasoning 布尔能力、context_length、参考图/视频上限、video capabilities、voices 与以 pollen 计价的pricing条目。访问权限判定modules-page.tsx#L251-L271是三态的const visibleModels isLoggedIn ? models.filter((model) allowedModelIds.has(modelId(model))) : models; // ... const selectedModelAccess !isLoggedIn ? Public catalog : selectedModel allowedModelIds.has(modelId(selectedModel)) ? Allowed by key : Not allowed;即游客看到完整公共目录并标记 Public catalog登录后只显示当前 keyallowedModelIds内的模型选中项标记 Allowed by key 或 Not allowed配合paid_only时的paidchip 与目录错误时的Alert intentwarning一起构成一套完整的状态展示。页面底部还有一个指向 Playground 的InlineLink。子路径导出的组织方式该页的 import 展示了pollinations/ui的分层子路径约定可作为二次开发时的参考根导出pollinations/uiprimitives 与 compositionsButton、Chip、Surface、Table、Alert等见 packages/ui/src/index.tspollinations/ui/markdownMarkdown/Prose渲染compositions-page.tsx 中使用pollinations/ui/gen模型选择、modality 相关组件pollinations/ui/app-user-menu/sdk与pollinations/ui/auth/sdk依赖PolliProvider上下文的 SDK 接线模块。Primitives 与 Compositions 视图两层的完整覆盖primitives-page.tsx 对pollinations/ui的 primitive 导出做了穷举展示每个示例都用 reference-layout.tsx 中定义的PrimitiveExample卡片包裹左侧名称描述、右侧可交互 demoTypographyHeadingsection 级Textsize: sm/xstone: soft/muted/strongButton / IconButtonCONTROL_SIZES [sm, md, lg]三档尺寸intentdanger危险变体Icons按 Brand / Modality / Capability / Billing / Actions / Navigation / Objects 七组展示全部导出图标DiscordIcon、GitHubIcon、ChatIcon、ReasoningIcon、TokensIcon等Chip尺寸 intent: neutral/warning/danger变体InlineLink内联链接页内导航与外链带箭头、新标签页打开区分Input / Field / Textarea单行输入、带 Label/HelperText/RequiredIndicator 的可访问字段包裹、多行输入Slider / Switch范围滑块与二态开关含statusinvalid展示ColorModeToggle全局明暗切换self-wired via the useColorMode hook and kept in sync across every instance and browser tabButtonGroup TabButton互斥 Tab 模式Dropdown DropdownItemtrigger/ children 均为 render prop 的锚定菜单Dialog DialogTitle支持sm/md/lg三种尺寸、受控open/onOpenChange的模态框Table 系列Table/TableHead/TableBody/TableRow/TableCell/TableHeaderCellSurfacevariantcard与card-themed两种卡片容器ScrollAreaaxisy的主题化滚动区TooltiptriggerAsspan的上下文提示。compositions-page.tsx 展示的是组合层——自带状态与行为的免 SDK 配方ExternalLinkButton保留给 checkout/授权等重要流程、CopyButtonrender prop 切换 copied 文案、LinkCard、NavItem、MultiSelect、PeriodPicker配currentPeriod()初始值、Collapsible、Alert、StatCard、Sectionframed 区块 action 槽、InfoTip、Markdown/Prose、CodeBlockcopy 动作独立在外、FileUploadmaxFiles{2}含校验与拒绝反馈、MediaPlaceholder生成媒体空态。两页的划分标准正是 README 的核心主张primitives 无逻辑compositions 有行为但无 SDKmodules 接 SDK。每层对应的源码目录分别为 packages/ui/src/primitives、packages/ui/src/compositions 与 packages/ui/src/modules内含app-user-menu、auth、gen、wallet四个模块。Colors 视图单强调色 固定角色色板虽然 README 只列了三个视图源码中的colors视图colors-page.tsx完整交代了设计系统的色彩规则值得单独理解Accent单强调色整个应用的所有主题 tokenbg-theme-*、border-theme-border、text-theme-text-soft等都由一个--polli-hue变量驱动页面文案写明当前是 hue 85amber琥珀色no per-page hue——即不允许逐页改色相Modalitymodality 固定色每个模型模态text/image/video/audio/realtime/embedding有固定颜色通过ModalityDot、ModalityChip、ModalityTab三种形态出现与强调色解耦identical wherever a modality appearsWallet钱包币色信用体系的两种 token——pollen与pollen对应var(--polli-color-tier-soft)与var(--polli-color-paid-soft)Structural结构色app-bg、surface-opaque、divider、text-strong/base/muted等中性色主题无关仅明暗切换影响Status labels意图色danger/success/warning交通灯三态 news/alpha标签色独立于强调色。页面源码注释还提示了一个 Tailwind 实践细节色卡类名必须写成字面量如bg-theme-bg-pale因为应用从源码扫描编译 Tailwind动态拼接的类名不会被产出。部署配置Cloudflare Pages 与工作区 watchdeploy.json 描述了该应用的自动化部署Cloudflare Pages 风格{ target: pages, credentials: apps, subdomain: react, install: npm install --silent npm install --silent --prefix ../.., build: npm run build, output: dist, watch: [packages/sdk/, packages/ui/, package.json, package-lock.json] }要点部署到react子域与config.ts中登记的https://react.pollinations.airedirect URI 对应install先装本应用依赖、再装仓库根monorepo依赖build走npm run build其prebuild钩子保证先构建packages/sdk与packages/ui产物输出到distwatch监听了两个工作区包目录意味着SDK 或 UI 包的变更也会触发本应用重新构建——这与其参考应用定位一致组件库一更新showcase 立即同步展示。小结从参考应用提炼的可复用模式apps/react虽然是展示应用但它固化了一套可以直接搬到自己项目中的 React Pollinations 集成模式分层引入 UI原子控件从pollinations/ui根导出取带行为配方用 compositions需要账号/模型/钱包能力时才引入/sdk子路径模块避免不必要的上下文依赖认证只配置两件事一个提交在代码里的pk_*publishable key记得为本地与生产分别登记 redirect URIPolliProvider appKey{...} permissions{[profile]}包裹PKCE 登录、state 校验、token 存储、401 自动登出都由 SDK 完成数据请求以enabled: isLoggedIn懒化游客零请求登录后才拉取 profile/key/usage/catalog本地联调 gen 后端只需设一个环境变量VITE_GEN_BASE_URLhttp://localhost:8788否则回退生产gen.pollinations.aimonorepo 里的 React 去重resolve.dedupe与部署时对 SDK/UI 包的 watch是保持多包一致性、避免 Invalid hook call 的两个低成本保险。本地开发只需在apps/react目录下执行npm run dev首次会先构建 SDK 与 UI 包与npm run typecheck即可完整体验上述所有视图与登录流程。赞分享AI 应用媒体生成后端前端【免费下载链接】pollinationsYour Friendly Open-Source Gen-AI Platform项目地址https://gitcode.com/gh_mirrors/po/pollinations点击查看免费下载相关推荐Open-Pencil Vue SDK API 参考全解Components、Composables 与 Advanced 三层 API 体系Open Pencil Vue SDK API 参考全解Components、Composables 与 Advanced 三层 API 体系 导读 本文以前端桌面应用AI 应用MCP 服务cgltf高级特性Draco压缩、MeshOpt优化与纹理格式支持详解cgltf高级特性Draco压缩、MeshOpt优化与纹理格式支持详解 cgltf是一款单文件的glTF 2.0加载器和写入器采用C99编写为开发者提供了图形学Amazon Bedrock Agentcore 前端集成Streamlit与React应用开发终极指南Amazon Bedrock Agentcore 前端集成Streamlit与React应用开发终极指南 Amazon Bedrock Agentcore 是示例工程人工智能AI Agent上一篇如何快速部署PilotGo-plugin-logs5分钟搭建企业级日志监控平台下一篇oeGitExt高级用法如何批量管理issues与自动化代码审查的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考