
前端后端数据科学开发工具【免费下载链接】jupyterlabJupyterLab computational environment.项目地址https://gitcode.com/gh_mirrors/ju/jupyterlab点击查看免费下载本篇教程以 JupyterLab 官方扩展教程为骨架带你从空白的开发环境出发使用jupyterlab/extension-template脚手架逐步搭建一个完整的应用插件application plugin它会在 JupyterLab 的命令面板Command Palette中新增一个 Random Astronomy Picture 命令激活时通过 NASA APOD API 获取随机日期的天文图片与元数据并在主工作区以可关闭、可恢复的标签页面板呈现。读完本文你将掌握扩展项目初始化、依赖注入requires/optional、命令系统、Widget 生命周期管理、布局状态恢复ILayoutRestorer以及 Python 打包发布PyPI / npm的完整链路具备独立开发并发布 JupyterLab 3/4 前端扩展的实战能力。本教程的每一步都有对应的可交互参考实现Plugin Playground也可以将最终成果与仓库中 JupyterLab 自身扩展的源码如packages/application-extension、packages/apputils-extension中的真实插件写法对照学习。教程目标与本仓库中的对应物我们要实现的扩展功能jupyterlab_apod包括向命令面板侧栏添加 Random Astronomy Picture 命令命令被激活时通过 NASA Astronomy Picture of the Day 服务获取图片与元数据在标签页面板中展示图片与元数据。在 JupyterLab 源码仓库中这类“应用插件”有大量现成范例例如 application-extension 中的插件通过requires: [ICommandPalette]等 token 声明依赖、apputils-extension 实现命令面板与布局恢复服务本身。学习完本教程后你可以回到这些目录对照真实生产代码理解教程简化之处背后的完整实现。一、搭建开发环境1.1 安装 miniconda教程假设你在 Linux 或 macOS 机器上操作Windows 用户需要对少量命令做调整。首先按照 Conda 官方安装文档 安装 miniconda。1.2 在 conda 环境中安装 NodeJS、JupyterLab 等为隔离项目依赖不要污染 miniconda 的 root 环境而是创建一个命名环境jupyterlab-ext其中包含最新版本的 JupyterLabcopier 及其依赖——用于从模板引导扩展项目结构的 Python 工具NodeJS——用于编译前端资源TypeScript、CSS的 JavaScript 运行时git——版本控制工具。创建环境的命令如下conda create -n jupyterlab-ext --override-channels --strict-channel-priority -c conda-forge -c nodefaults jupyterlab4 nodejs20 git copier9 jinja2-time --yes创建完成后激活该环境每个新开的终端都需要重新激活conda activate jupyterlab-ext二、创建扩展项目2.1 可选创建远程仓库如果你打算与他人分享扩展建议先创建一个 git 远程仓库例如在 GitHub 上。这一步可选但强烈推荐。2.2 用 copier 从模板初始化项目在扩展目录中执行mkdir my_first_extension cd my_first_extension copier copy --trust https://github.com/jupyterlab/extension-template .copier会逐项提问按下面的示例回答apod即 Astronomy Picture of the Day 的缩写指代我们使用的 NASA 服务What is your extension kind? (Use arrow keys) frontend Extension author name Your Name Extension author email yourname.org JavaScript package name jupyterlab_apod Python package name jupyterlab_apod Extension short description Show a random NASA Astronomy Picture of the Day in a JupyterLab panel Does the extension have user settings? N Do you want to set up Binder example? Y Do you want to set up test for the extension? Y Git remote repository URL https://github.com/github_username/jupyterlab_apod几点说明如果暂不使用远程仓库仓库字段可以留空日后在package.json中再补充较新版本的模板默认附带测试若不需要可对测试提问回答n。用ls -a查看生成的项目结构应看到类似以下列表.copier-answers.yml .github .gitignore .prettierignore .yarnrc.yml babel.config.js jest.config.js pyproject.toml src ui-tests binder jupyterlab_apod README.md style yarn.lock CHANGELOG.md LICENSE RELEASE.md tsconfig.json install.json package.json setup.py tsconfig.test.json2.3 用 git 提交初始状态git init git add . git commit -m Seed apod project from extension template这一步并非必须但良好的版本控制习惯能在需要回滚或协作时帮上大忙本教程后续每一步都有对应的参考 commit 可对照。2.4 构建并安装扩展开发模式模板自带足够的代码让你立刻看到扩展生效pip install -ve .该命令会把扩展的前端部分复制进 JupyterLab。每次修改后都可以重新执行它更推荐用develop模式创建从 JupyterLab 到源码目录的符号链接使改动自动生效jupyter-builder develop --overwrite .Windows 用户注意符号链接需要 Windows 10 及以上系统开启“开发者模式”Python 3.8且可能受管理员策略限制请参考微软官方 Activate Developer Mode on Windows 说明。2.5 验证初始扩展生效另开一个终端激活环境并启动 JupyterLabconda activate jupyterlab-ext jupyter lab浏览器打开 JupyterLab 后打开开发者工具的 JavaScript 控制台Chrome 的 DevTools 或 Firefox 的 Web Console刷新页面应能看到JupyterLab extension jupyterlab_apod is activated!看到这条日志说明环境就绪可以开始改代码了。建议让运行jupyter lab的终端保持开启以便随时观察后续改动效果。三、认识插件骨架JupyterFrontEndPlugin动手前先理解模板生成的src/index.ts核心结构。plugin对象的类型是JupyterFrontEndPluginvoid在 JupyterLab 源码中定义为见 packages/application/src/frontend.tsexport type JupyterFrontEndPlugin T, U extends JupyterFrontEnd.IShell JupyterFrontEnd.IShell, V extends string desktop | mobile IPluginJupyterFrontEndU, V, T;它继承自 Lumino 的IPlugin本质上是“给JupyterFrontEnd应用注册的一段可激活代码”。一个插件通常包含id全局唯一的插件标识符description插件描述显示在扩展管理器等界面autoStart是否随应用启动自动激活requires激活时必需的服务 token 列表JupyterLab 会把对应服务实例按声明顺序注入activate函数的参数optional可选服务 token如本教程后面用到的ILayoutRestoreractivate插件激活函数接收应用实例与所请求的服务实例。这些字段与仓库中 packages/application/src/lab.ts 里ILuminoPluginData对插件id | description | autoStart的建模一致说明插件元数据是 JupyterLab 插件系统的一等公民。四、显示一个空面板接入命令面板4.1 声明对 ICommandPalette 的依赖命令面板是 JupyterLab 中所有命令的“总览视图”。第一步是让扩展请求命令面板服务。修改src/index.ts顶部导入import { JupyterFrontEnd, JupyterFrontEndPlugin } from jupyterlab/application; import { ICommandPalette } from jupyterlab/apputils;然后将plugin对象修改为/** * Initialization data for the jupyterlab_apod extension. */ const plugin: JupyterFrontEndPluginvoid { id: jupyterlab-apod, description: Show a random NASA Astronomy Picture of the Day in a JupyterLab panel., autoStart: true, requires: [ICommandPalette], activate: (app: JupyterFrontEnd, palette: ICommandPalette) { console.log(JupyterLab extension jupyterlab_apod is activated!); console.log(ICommandPalette:, palette); } };原理requires声明插件启动时需要实现ICommandPalette接口的对象JupyterLab 会把该实例作为activate的第二个参数传入因此函数签名里写palette: ICommandPalette即可拿到命令面板。ICommandPalette是定义在 packages/apputils/src/tokens.ts 中的Tokenjupyterlab/apputils:ICommandPalette其接口同文件 L31-L50提供placeholder、activate()与addItem(options)三个成员后面palette.addItem(...)正是调用该接口。安装新依赖并重新构建jlpm add jupyterlab/apputils jupyterlab/application jlpm buildjlpm是 JupyterLab 自带封装的yarn你也可以用npm或yarn代替。刷新浏览器标签页后控制台应输出JupyterLab extension jupyterlab_apod is activated! ICommandPalette: Palette {_palette: CommandPalette}关于构建的两阶段说明jlpm build实际做两件事——先把src/下的 TypeScript 编译为lib/下的 JavaScript即jlpm build再把lib/打包成 JupyterLab 扩展jlpm build:extension产物位于jupyterlab_apod/static。如果想省去每次手动构建可再开一个终端激活环境后运行jlpm watch它会监听文件变化自动编译。4.2 创建 MainAreaWidget 并注册命令现在让命令真正产生一个面板。补充导入import { ICommandPalette, MainAreaWidget } from jupyterlab/apputils; import { Widget } from lumino/widgets;安装依赖jlpm add lumino/widgets将activate函数体替换为注意保持文件底部export default plugin;不变只改函数内容、花括号要配对activate: (app: JupyterFrontEnd, palette: ICommandPalette) { console.log(JupyterLab extension jupyterlab_apod is activated!); // Define a widget creator function, // then call it to make a new widget const newWidget () { // Create a blank content widget inside of a MainAreaWidget const content new Widget(); const widget new MainAreaWidget({ content }); widget.id apod-jupyterlab; widget.title.label Astronomy Picture; widget.title.closable true; return widget; } let widget newWidget(); // Add an application command const command: string apod:open; app.commands.addCommand(command, { label: Random Astronomy Picture, execute: () { // Regenerate the widget if disposed if (widget.isDisposed) { widget newWidget(); } if (!widget.isAttached) { // Attach the widget to the main work area if its not there app.shell.add(widget, main); } // Activate the widget app.shell.activateById(widget.id); } }); // Add the command to the palette. palette.addItem({ command, category: Tutorial }); }代码分三块widget 创建函数返回一个MainAreaWidget其内容子部件是一个空Widget同时给主区部件设置唯一 ID、标签标题作为标签页标题显示并允许用户关闭该标签页。MainAreaWidget在源码中的定义见 packages/apputils/src/mainareawidget.ts其注释明确说明它“镜像 content 的所有 title 属性、默认可关闭、关闭时自动销毁、激活时自动获得焦点”。命令注册app.commands.addCommand(apod:open, {...})注册命令。执行逻辑先检查 widget 是否已被销毁isDisposed是则重建若尚未挂载!isAttached则通过app.shell.add(widget, main)挂到主工作区最后app.shell.activateById(widget.id)令其成为活动标签页。加入命令面板palette.addItem({ command, category: Tutorial })把命令放进命令面板的Tutorial分类下command即命令 ID。执行jlpm build或在用jlpm watch则无需手动执行刷新浏览器。通过菜单View → Commands或快捷键Command/Ctrl Shift C打开命令面板搜索Astronomy点击Random Astronomy Picture命令即可看到标题为Astronomy Picture的空面板出现。验证行为点击标签页上的x关闭面板后再运行命令面板应重新出现先点击启动器launcher让Astronomy Picture面板处于非活动状态再运行命令单个面板应回到前台——这说明“若已存在则不重复创建、只做激活”的逻辑正确。一切正常后提交git add package.json src/index.ts git commit -m Show Astronomy Picture command in palette五、在面板中显示图片异步 fetch 与类型定义5.1 加入图片元素与 APOD API 调用在 widget 创建函数中MainAreaWidget创建之后、return widget;之前插入// Add an image element to the content let img document.createElement(img); content.node.appendChild(img); // Get a random date string in YYYY-MM-DD format function randomDate() { const start new Date(2010, 1, 1); const end new Date(); const randomDate new Date( start.getTime() Math.random() * (end.getTime() - start.getTime()) ); return randomDate.toISOString().slice(0, 10); } // Fetch info about a random picture const response await fetch( https://api.nasa.gov/planetary/apod?api_keyDEMO_KEYdate${randomDate()} ); const data (await response.json()) as APODResponse; if (data.media_type image) { // Populate the image img.src data.url; img.title data.title; } else { console.log(Random APOD was not a picture.); }逻辑拆解前两行创建一个 HTMLimg元素并挂到 widget 的 DOM 节点上randomDate()生成 2010 年 1 月 1 日到当前日期之间的随机日期字符串YYYY-MM-DD格式用浏览器原生 Fetch API 请求 NASA APOD 接口api_key使用DEMO_KEY演示密钥有请求频率限制根据响应中的media_type判断返回的是图片还是视频是图片则设置img.src与img.title。5.2 定义响应类型在文件顶部导入语句下方定义interface APODResponse { copyright: string; date: string; explanation: string; media_type: video | image; title: string; url: string; }5.3 补齐 async/await由于 widget 创建函数中使用了await需要把相关函数改为异步activate: async (app: JupyterFrontEnd, palette: ICommandPalette) {const newWidget async () {命令执行函数同样改为 async并在两处调用处加awaitlet widget await newWidget(); // Add an application command const command: string apod:open; app.commands.addCommand(command, { label: Random Astronomy Picture, execute: async () { // Regenerate the widget if disposed if (widget.isDisposed) { widget await newWidget(); } if (!widget.isAttached) { // Attach the widget to the main work area if its not there app.shell.add(widget, main); } // Activate the widget app.shell.activateById(widget.id); } });对 JavaScript/TypeScript 的async、await、Promise 不熟悉的读者可参考 MDN 的 Promise 教程。重新构建并刷新页面运行命令即可看到随机日期的 APOD 图片如果该日期返回的是视频则会跳过。注意此时图片尚未居中面板在图片超出区域时也不会滚动——这两点将在下一节解决。git add src/index.ts git commit -m Show a picture in the panel六、完善组件行为样式、署名与错误处理6.1 添加 CSS打开扩展项目中的style/base.css追加.my-apodWidget { display: flex; flex-direction: column; align-items: center; overflow: auto; }这段 CSS 让面板内容垂直堆叠、水平居中并在内容溢出时允许滚动。自动生效的机制该 CSS 文件之所以能被 JupyterLab 自动加载是因为package.json中的style字段指向了它一般实践是把所有样式集中到一个 CSS 文件例如index.css再把路径填进package.json的style字段。6.2 应用样式类、版权署名与错误处理回到src/index.ts重写activate函数开头部分activate: async (app: JupyterFrontEnd, palette: ICommandPalette) { console.log(JupyterLab extension jupyterlab_apod is activated!); // Define a widget creator function, // then call it to make a new widget const newWidget async () { // Create a blank content widget inside of a MainAreaWidget const content new Widget(); content.addClass(my-apodWidget); const widget new MainAreaWidget({ content }); widget.id apod-jupyterlab; widget.title.label Astronomy Picture; widget.title.closable true; // Add an image element to the content let img document.createElement(img); content.node.appendChild(img); let summary document.createElement(p); content.node.appendChild(summary); // Get a random date string in YYYY-MM-DD format function randomDate() { const start new Date(2010, 1, 1); const end new Date(); const randomDate new Date(start.getTime() Math.random()*(end.getTime() - start.getTime())); return randomDate.toISOString().slice(0, 10); } // Fetch info about a random picture const response await fetch(https://api.nasa.gov/planetary/apod?api_keyDEMO_KEYdate${randomDate()}); if (!response.ok) { const data await response.json(); if (data.error) { summary.innerText data.error.message; } else { summary.innerText response.statusText; } } else { const data await response.json() as APODResponse; if (data.media_type image) { // Populate the image img.src data.url; img.title data.title; summary.innerText data.title; if (data.copyright) { summary.innerText (Copyright ${data.copyright}); } } else { summary.innerText Random APOD fetched was not an image.; } } return widget; } // 从 newWidget 函数定义往下其余代码保持不变变化要点content.addClass(my-apodWidget)应用刚写的 CSS 类新增p元素summary用于展示标题与版权信息增加response.ok检查请求失败时优先展示 API 返回的error.message否则展示response.statusText成功时显示图片标题并在存在copyright字段时追加(Copyright xxx)若media_type不是图片则提示 Random APOD fetched was not an image.。提示如果面板一直显示错误信息很可能是DEMO_KEY的请求配额被用尽每小时请求次数有限可以考虑申请个人 NASA API Key。构建刷新后确认图片已居中、版权信息显示在图片下方并缩放浏览器/面板让图片超出可视区域验证面板可以滚动查看完整图片。git add style/base.css src/index.ts git commit -m Add styling, attribution, error handling七、重构为 APODWidget 类并支持按需刷新activate函数已经很长且我们还需要“再次执行命令时刷新图片”的能力。重构思路是把代码拆成两部分APODWidget类封装天文图片面板的元素、配置与更新行为activate函数负责把 widget 实例加入 UI 并决定何时刷新。7.1 定义 APODWidget 类在APODResponse定义下方新增类class APODWidget extends Widget { /** * Construct a new APOD widget. */ constructor() { super(); this.addClass(my-apodWidget); // Add an image element to the panel this.img document.createElement(img); this.node.appendChild(this.img); // Add a summary element to the panel this.summary document.createElement(p); this.node.appendChild(this.summary); } /** * The image element associated with the widget. */ readonly img: HTMLImageElement; /** * The summary text element associated with the widget. */ readonly summary: HTMLParagraphElement; /** * Handle update requests for the widget. */ async updateAPODImage(): Promisevoid { const response await fetch( https://api.nasa.gov/planetary/apod?api_keyDEMO_KEYdate${this.randomDate()} ); if (!response.ok) { const data await response.json(); if (data.error) { this.summary.innerText data.error.message; } else { this.summary.innerText response.statusText; } return; } const data (await response.json()) as APODResponse; if (data.media_type image) { // Populate the image this.img.src data.url; this.img.title data.title; this.summary.innerText data.title; if (data.copyright) { this.summary.innerText (Copyright ${data.copyright}); } } else { this.summary.innerText Random APOD fetched was not an image.; } } /** * Get a random date string in YYYY-MM-DD format. */ randomDate(): string { const start new Date(2010, 1, 1); const end new Date(); const randomDate new Date( start.getTime() Math.random() * (end.getTime() - start.getTime()) ); return randomDate.toISOString().slice(0, 10); } }这段代码就是把上一节写在函数里的逻辑搬到类中用实例变量img、summary持有 DOM 元素图片请求独立成updateAPODImage()方法随机日期生成独立成randomDate()方法。7.2 精简 activate 函数在类定义下方重写顶层activate函数逻辑变为“面板不存在则创建已存在则刷新图片”/** * Activate the APOD widget extension. */ function activate(app: JupyterFrontEnd, palette: ICommandPalette) { console.log(JupyterLab extension jupyterlab_apod is activated!); // Define a widget creator function const newWidget () { const content new APODWidget(); const widget new MainAreaWidget({ content }); widget.id apod-jupyterlab; widget.title.label Astronomy Picture; widget.title.closable true; return widget; }; // Create a single widget let widget newWidget(); // Add an application command const command: string apod:open; app.commands.addCommand(command, { label: Random Astronomy Picture, execute: () { // Regenerate the widget if disposed if (widget.isDisposed) { widget newWidget(); } if (!widget.isAttached) { // Attach the widget to the main work area if its not there app.shell.add(widget, main); } // Refresh the picture in the widget widget.content.updateAPODImage(); // Activate the widget app.shell.activateById(widget.id); } }); // Add the command to the palette. palette.addItem({ command, category: Tutorial }); }注意命令执行逻辑新增的一行widget.content.updateAPODImage();——每次执行命令都会为现有面板拉取一张新图。MainAreaWidget.content即构造时传入的内容部件这里就是APODWidget实例。7.3 更新插件定义把JupyterFrontEndPlugin对象里的activate从内联函数改为引用顶层函数并保留export default plugin;const plugin: JupyterFrontEndPluginvoid { id: jupyterlab_apod, autoStart: true, requires: [ICommandPalette], activate: activate };构建刷新后验证不关闭面板连续多次运行命令图片应每次更新关闭面板后再运行命令面板应重新出现且显示新图。git add src/index.ts git commit -m Refactor, refresh image八、刷新浏览器后恢复面板状态你可能会注意到刷新浏览器标签页后已打开的 APOD 面板会消失而笔记本、终端、文本编辑器等面板都会按原布局恢复。通过ILayoutRestorer可以让你的扩展也具备状态恢复能力。8.1 更新导入import { ILayoutRestorer, JupyterFrontEnd, JupyterFrontEndPlugin } from jupyterlab/application; import { ICommandPalette, MainAreaWidget, WidgetTracker } from jupyterlab/apputils; import { Widget } from lumino/widgets;8.2 将 ILayoutRestorer 声明为 optional 依赖ILayoutRestorer的接口定义见 packages/application/src/layoutrestorer.ts它提供restored就绪 Promise、add(widget, name)注册单部件、以及restore(tracker, options)恢复某个WidgetTracker的全部部件。const plugin: JupyterFrontEndPluginvoid { id: jupyterlab-apod, description: Show a random NASA Astronomy Picture of the Day in a JupyterLab panel., autoStart: true, requires: [ICommandPalette], optional: [ILayoutRestorer], activate: activate };这里把ILayoutRestorer放进optional而非requires布局恢复服务在个别定制版 JupyterLab 应用中可能不存在声明为可选能让扩展在更多 JupyterLab 衍生应用中正常加载是“锦上添花”而非“必要依赖”。requires与optional的完整机制可参考本仓库 Extension Developer Guide 中关于 tokens 的章节。8.3 重写 activate跟踪与恢复function activate( app: JupyterFrontEnd, palette: ICommandPalette, restorer: ILayoutRestorer | null ) { console.log(JupyterLab extension jupyterlab_apod is activated!); // Declare a widget variable let widget: MainAreaWidgetAPODWidget; // Add an application command const command: string apod:open; app.commands.addCommand(command, { label: Random Astronomy Picture, execute: () { if (!widget || widget.isDisposed) { const content new APODWidget(); widget new MainAreaWidget({ content }); widget.id apod-jupyterlab; widget.title.label Astronomy Picture; widget.title.closable true; } if (!tracker.has(widget)) { // Track the state of the widget for later restoration tracker.add(widget); } if (!widget.isAttached) { // Attach the widget to the main work area if its not there app.shell.add(widget, main); } widget.content.updateAPODImage(); // Activate the widget app.shell.activateById(widget.id); } }); // Add the command to the palette. palette.addItem({ command, category: Tutorial }); // Track and restore the widget state let tracker new WidgetTrackerMainAreaWidgetAPODWidget({ namespace: apod }); if (restorer) { restorer.restore(tracker, { command, name: () apod }); } }关键点解读widget只声明不立即创建实例由命令首次执行时惰性创建第三个参数restorer类型为ILayoutRestorer | null与optional声明对应服务可能缺失新建WidgetTracker命名空间apod记录 widget 的保存/恢复状态每次创建 widget 后通过tracker.add(widget)纳入跟踪restorer.restore(tracker, { command, name: () apod })告诉布局恢复器刷新后通过执行apod:open命令重建 widgetname返回稳定标识符。恢复机制的底层流程依据 packages/application/src/layoutrestorer.ts 的实现注释布局恢复插件在应用启动时先fetch持久化的布局数据加载期插件向它注册各自要恢复的 tracker应用started后 restorer 逐个要求 tracker 恢复状态——而状态恢复是通过执行命令完成的因此这里传给restore的command必须是一个“创建 widget 并加入 tracker”的命令apod:open恰好满足全部 tracker 恢复完成后restorer 把布局状态交给应用 shell 的restoreLayout最终应用级restoredPromise 才会 resolve。这解释了为什么教程要求“用于恢复的命令必须能幂等地重建 widget 并跟踪它”。构建并刷新验证运行命令打开面板 → 刷新浏览器 → 面板应立即自动重新出现无需再运行命令关闭面板 → 刷新浏览器 → 刷新后不应再出现该标签页。git add src/index.ts git commit -m Restore panel state至此教程开头规划的所有行为命令面板入口、拉取图片与元数据、标签页展示、状态恢复全部实现完毕。九、将扩展打包为 Python 包JupyterLab 3.0 及以上版本的扩展可以以Python 包形式分发。模板已在pyproject.toml中写好了全部打包配置。先生成构建工具pip install build创建源码包.tar.gz输出到dist/python -m build -s创建 wheel 包.whl输出到dist/python -m build两条命令都会先把 JavaScript 打包进jupyterlab_apod/labextension/static目录随 Python 包一起分发该 bundle 会包含所有必要的 JavaScript 依赖。你可以选择把labextension/static提交进版本库以记录分发的具体 JS 内容也可以把它当作构建产物排除在源码历史之外。像普通用户一样验证安装新开终端创建全新环境并安装 wheelconda create -n jupyterlab-apod jupyterlab conda activate jupyterlab-apod pip install jupyterlab_apod/dist/jupyterlab_apod-0.1.0-py3-none-any.whl jupyter lab新环境启动的 JupyterLab 中运行Random Astronomy Picture命令确认扩展工作正常。十、发布扩展10.1 发布渠道Python 包发布到 PyPI 或 conda-forge用户即可通过pip install/conda install安装JavaScript 包发布到 npm。理由有二用户可以把扩展显式编译进JupyterLab类似 JupyterLab 1/2 时代的做法得到更优的打包结果更重要地JupyterLab 允许扩展之间互相提供/消费服务本教程的扩展就消费了核心扩展提供的ICommandPalette、ILayoutRestorer服务通过从jupyterlab/apputils、jupyterlab/applicationnpm 包导入 token 并在插件定义中声明。如果你想让自己的服务 token 被其他扩展导入使用就必须把 JavaScript 包发布到 npm其他扩展才能声明依赖并 import 你的 token。10.2 自动化发布用模板引导的项目已兼容 Jupyter Releaser。它提供一组 GitHub Actions 工作流可自动完成在 Changelog 中生成新条目起草新版本 release将版本发布到 PyPI 与 npm。具体运行方式参见 Jupyter Releaser 的文档。十一、下一步可以做什么把 API 响应中的图片描述explanation也渲染到面板中为Random Astronomy Picture命令分配默认快捷键让图片可点击跳转到 NASA 网站对应页面URL 形如https://apod.nasa.gov/apod/apYYMMDD.html在图片加载完成后同步更新标题与描述保证图文一致允许用户把喜欢的图片“钉”在独立、常驻的面板中新增用户设置项让用户填写自己的 NASA API KeyDEMO_KEY 有每小时请求次数限制把扩展仓库推送到 GitHub开始维护与协作进一步学习本仓库 Extension Developer Guide 中关于 其他类型扩展如文档级扩展、渲染器扩展、前端服务 token 与 扩展国际化 的章节编写更复杂的扩展。附录参考实现与调试资源教程每一步的参考代码都对应jupyterlab_apod参考仓库的 git tag4.0-01-show-a-panel、4.0-02-show-an-image、4.0-03-style-and-attribute、4.0-04-refactor-and-refresh、4.0-05-restore-panel-state代码出错时可逐 tag 对照也可以把每一步的src/index.ts加载进 Plugin PlaygroundJupyterLite 环境的在线扩展调试器在浏览器里直接运行、调试各步骤的扩展代码本仓库中 JupyterLab 自带的 application-extension 与 apputils-extension 是生产级插件范例其中对ICommandPalette、ILayoutRestorer、WidgetTracker的实际使用方式值得深入研读。赞分享前端后端数据科学开发工具【免费下载链接】jupyterlabJupyterLab computational environment.项目地址https://gitcode.com/gh_mirrors/ju/jupyterlab点击查看免费下载相关推荐JupyterLab扩展开发实战从零构建自定义插件JupyterLab扩展开发实战从零构建自定义插件 本文深入探讨JupyterLab扩展开发的完整流程从架构设计理念到实际开发部署。首先解析JupyterL前端后端数据科学开发工具Wand-EnhancerWeMod本地化功能扩展解决方案Wand EnhancerWeMod本地化功能扩展解决方案 Wand Enhancer是一个开源的本地方案增强工具专门为WeMod游戏修改器平台提供扩展功能桌面应用前端如何快速构建你的第一个Oni编辑器插件从零开始的完整指南如何快速构建你的第一个Oni编辑器插件从零开始的完整指南 Oni编辑器是一款基于Neovim的现代模态编辑器它为开发者提供了强大的插件扩展能力。通过Oni插开发工具代码编辑器桌面应用创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考