Coder Social home page Coder Social logo

bioforestchain / dweb_browser Goto Github PK

View Code? Open in Web Editor NEW
11.0 8.0 4.0 272.5 MB

BioforestChain Infrastructure

Home Page: https://docs.dweb-browser.org

License: MIT License

TypeScript 18.16% JavaScript 0.10% HTML 0.33% Vue 3.72% CSS 0.02% Kotlin 67.32% SCSS 0.97% Swift 8.68% Shell 0.28% Objective-C 0.43%
browser dweb application blockchain kotlin-multiplatform plaoc

dweb_browser's Introduction


Dweb-Browser

ENGLISH DOC

stable-release stable-release starts Plaoc Group

About dweb-browser

dweb-browser 是一个遵循 Dweb 标准构建起来的浏览器平台,并将浏览器的能力、以及浏览器相关的系统原生系统能力通过各种 dweb 模块暴露出来。目前我们实现了 Android、IOS、MacOS、Windows、Linux 这些主流平台的支持。 它包含一下这些核心模块:

js.browser.dweb

它是一个 javascript-runtime,使用的是 WebWorker 作为底层实现。因此 WebWorker 中的各种标准都可以开箱即用。

jmm.browser.dweb

它是一个动态 dweb 模块管理器,基于此可以实现类似 PWA 的应用功能

mwebview.browser.dweb

它的全称是 mutil-webview(多 web 视图)的渲染器,可以使用这个渲染器同时渲染多个 Web 视图。比如说可以用它实现一个网页浏览器。

desk.browser.dweb

它是一个 dweb-browser 自己实现的窗口标准 window.std.dweb,在跨平台上保持一致性的窗口体验。

*.sys.dweb

和浏览器相关的一些系统标准也在 dweb-browser 上被实现。

什么是 plaoc

plaoc 是基于 dweb-browser 平台的一个对标 Cordova、Capacitor、Tauri 的“跨平台 Web 应用”开发工具包

cli

@plaoc/cli 是 plaoc 开发并打包应用到 dweb_browser 的命令行工具。

  1. npm i -g @plaoc/cli

  2. plaoc bundle ./dir 会打包成以下的文件夹结构,并输出压缩文件 .zip 和一个 plaoc.metadata.json

  3. plaoc preview http://localhost:1231 或者 plaoc preview ./dir

    会将该 url 放在一个 iframe 中被预览 该命令会输出一行命令:

    dweb-browser-dev install --url http://172.30.90.240:8080/usr/metadata.json

plugins

@plaoc/plugins 能赋予 web 开发者,直接调用各个平台系统 API 的能力。

具体文档查看:plugins 文档

Q&A

mac 桌面端如果出现: “Dweb Browser” 已损坏,无法打开。 你应该将它移到废纸篓。

可以使用下面命令运行。

sudo xattr -d com.apple.quarantine /Applications/Dweb\ Browser.app

更多问题可以查看文档dweb_browser

dweb_browser's People

Contributors

charlieatinstinct avatar dependabot[bot] avatar gaubee avatar huang493 avatar jackie-yellow avatar kingsword09 avatar pengxiaohua575527452 avatar waterbang avatar xing123456789 avatar yangfan100 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

dweb_browser's Issues

12月上线IOS打磨计划

打磨计划

必要工作

  • 完成 Taskbar 的完整适配
    • Taskbar 的背景
      通过在 taskbarView 上增加特定的功能来解决。
    • Taskbar 的拖拽
  • 修复 WKWebView 不支持复合手势问题
    • 长按手势 的修复
    • CloseWatcher 的修复
  • 修复 kotlin.Exception: no found response by req_id:
  • startURLSchemeTask 还是存在 closed 异常

    在 objc 中实现错误捕捉,然后在 swift 中将捕捉到的错误处理掉

  • Android setContentScale 对于 histroy 有 bug
  • IOS 上 setContentScale 不工作
  • IOS 上 UIKItView 的 transform、opacity 不工作
  • bottomSheet 需要提供 mmid 信息
  • Deeplink 在 IOS 平台的的支持
  • TaskbarView 在 IOS 上会一直处于 contextmenu 状态
  • IOS 中应用关闭后,就无法重新启动了
  • IOS 中 WKWebView 的 SafeAreaInsets 实现
  • IOS 中 DesktopView 的长按无法通过点击旁边的留白区域取消弹出层
  • jmm 的 bottomSheet 图片预览打开后无法关闭
  • Android WebView 支持 SafeArea

    代码层面已经实现,但是就是不能用

  • http.std.web/websocket
  • http.std.web/fetch
  • 改进 userAgent
  • Android 使用 WebViewCompat 的 addDocumentStartJavaScript 优化流程
  • WKWebView 没有正确地被自动释放
  • 为什么扬帆应用会出现 ALL_PORT 重复定义的错误,这是 WebMessagePort 垫片重复执行的问题

可选工作

  • bottomSheet 在 IOS 的背景遮罩没有置顶
  • 使用 Swift-BrowserView 替代现有的 Compose 开发的浏览器
  • Swift-BrowserView 能正确与适应 WindowRenderScope
  • IPC 架构升级
    • 支持 Fork:并用它替代现有的大部分 ReadableStreamIPC
    • 支持 Lifecycle:用它替代现有的 ready 轮询机制
  • Not yet implemented SetSystemBarsColor
  • Not yet implemented EffectKeyboard
  • Not yet implemented EffectNavigationBar
  • Not yet implemented EffectSafeModel
  • Not yet implemented NativeBackHandler
  • Not yet implemented SingleChoiceSegmentedButtonRow
  • 使用 WebViewCompat 来管理 WebMessagePort
  • IOS 中,窗口拖拽手势不能很好地在切换窗口层级的同时工作
  • Taskbar 的背景(高斯模糊与背景色)在应用全屏、关闭之间切换的时候,会出现渲染错误
  • IOS 中卸载重装偶尔会出现白屏不渲染 webview 的情况,Android 也会这样?onRenderer 没有正确传递过来

【提案】♻️ uuid 的定义与存储

当下,UUID 的逻辑是这样的:

  1. 如果 AppContext 没有初始化的 UUID
    1. 查找外部磁盘中是否存在备份
      1. 如果外部磁盘不存在备份
        1. 初始化 UUID 到 AppContext 中保存
        2. 并写入到外部磁盘中进行持久化备份
      2. 如果外部磁盘存在备份
        1. 读取备份信息,并将它恢复到 AppContext 中保存
  2. 如果 AppContext 已经初始化过 UUID,那么直接使用该 UUID 信息

    无视外部备份的修改

    未来可以实现 Dweb 备份导出导入的功能

这个方案有几个点值得改进:

  1. 目前保存到 AppContext 脱离了 file.std.dweb 的文件体系,未来做备份恢复,或者跨设备同步的时候会存在问题
  2. UUID 的保存不该是明文
    1. Dweb 未来作为一个 OS,那么其实是应该有一个本地用户系统
    2. 用户视角里的 UUID,其实应该指代用户自己,而不是用户的手机设备,在更换设备的时候,uuid 应该跟着“用户”走而不是设备。
    3. 基于以上两点,可以构想出未来 UUID 的标准:
      1. 首先用户在启动 dweb-browser 的时候,其实应该是在启动 dweb-os,那么需要初始化用户信息与密钥
        1. 密钥 = 主密钥+pin
        2. 提供一些安全信息,来做主密钥的生成,因此我们需要内置一些常见的问答题目,或者用户可以自定义题目。题目将单独明文存储,而其题目和答案将作为主密钥生成的基础。
        3. 要求用户一个提供易于记忆的 pin 码,来作为安全密码,安全密码的作用是对主密钥进行加密
        4. 如果硬件支持,可以将 pin 码存储在安全的硬件中
        5. 这里务必要提示用户,这里所有的数据不会有任何服务端主动接入进行备份,因此需要用户自己做好备份服务。但是未来可以提供系统级别的备份模块,那么第三方就可以介入帮助用户在云端做备份。
      2. 拥有密钥后,UUID 就有了加密的凭证
        1. 首先 UUID 还是随机生成,这个不可避免,不能使用密钥,因为密钥是用户自己主观意识生成的,不能确保它的唯一性,只能确保它的私密性,因此 UUID 需要随机生成从而确保唯一性
        2. 其次,在保存到外部磁盘的时候,我们需要对 UUID 进行加密存储,避免被别人在外部恶意修改,但是被删除是不可避免的,但至少不会被篡改
  3. 还有一个提议值得商榷,我觉得应该把 UUID 存储到“联系人”信息中,这是可以跟着设备联系人备份功能走的,更加容易形成迁移

最后说个题外话,生态内的应用如果要使用 UUID 来作为壁垒,那么就应该为 UUID 赋予价值,最常见的就是提供标准,让第三方平台可以对 UUID 进行实名认证。越多元的标准与这个 UUID 绑定在一起,那么这个 UUID 就越发有价值,就像人的信用一样,需要有一个培养的过程。否则任何优惠业务想基于 UUID 来展开,如果 UUID 自身没有壁垒,没有价值,那么薅羊毛的问题是不可能被解决的。

multipart.http.std.dweb

实现对http对Multipart标准的模块化实现

要求:

  1. 能够做按需解析,从而做到快速响应
  2. 对异常有足够好的包容性
  3. 解析做到可配置,比如配置大小限制、配置类型限制、配置缓存目录

参考资料:
https://github.com/abonander/multipart

【提案】🎉 shortcut.sys.dweb

  1. ✨ ShortcutManagerController/.Render 排序、管理要显示在原生上的项目
  2. /registry?title=*&uri=*&icon?=* 提供功能注册,我们需要根据对方的 mmid+version 来进行保存
    • icon: 使用对方的 app-icon 做为默认图标,如果对方自己提供了 shortcut-icon,那么将 app-icon 做为 shortcut-icon 的角标
  3. ✨ 每次 dweb browser 启动的时候,都要遍历 shortcut 的对应的应用是否被卸载,从而移除相应的图标注册(可以考虑移动到 tmp 文件夹中,如果应用恢复,那么可以找回;否则空间回收时被清理)
  4. ✨ 监听 dns 安装到应用的变更
  5. ✨ 提供管理界面,允许用户来拖动排序这些 shortcut,并且最终反应到原生平台上的 shortcut 可定是有限额度的,所以用户可以看到哪些 shortcut 是被启用的
  6. ✨ 如果 shortcut 过多,我们在原生平台上至少要留一个“更多”,打开后能直接显示 shortcut.sys.dweb 的主界面,用户能找到所有的 shortcut
  7. ❓ 有没有可能实现声明式的 shortcut

参考资料:

让 IPC 支持 SSE

IpcRequest用来发起连接
IpcResponse用来回应连接,并且基于 IpcEvent 对象提供内容

【提案】 🎉 id-card.std.dweb

  1. 这只是一个存储器,但是是专门用户存储个人网络信息
  2. 可以存储多个身份,这里的身份就是一个卡片
    1. 卡片本质就是一个 key-value 存储器
    2. 对于 key:软件也会内置一些常见的字段名称:“名字”、“出生日期” 等,但这不是强制的,只是一个推荐性的文本,这种推荐文本会有多语言的功能
    3. 用户可以自己设计卡片字段,除了常见的文本类型:文本、数字、时间、邮箱、号码、账号 ID、密码 等,还会有功能性类型:“密钥”
    4. 密钥一般会有两种
      1. 一种是身份验证器,这一般是服务端生成密钥给用户,就是服务端与客户端使用统一的算法,每隔一分钟会生成一段数字作为密码,这种一般是服务端提供密钥
      2. 一种是签名密钥,这一般用户生成密钥然后将公钥给服务端,服务端每次需要认证时,提供一串有意义的文本给客户端,让客户端为它签名,用户通常需要仔细阅读这个文本
    5. 在使用的卡片的时候,调用者需要提供需要输入的字段以及类型,那么会自动尝试关联,否则需要用户通过拖拽来将卡片中的信息拖入,这种拖拽会形成一种关联性,基于这种关联性,可以进行机器学习,从而形成自动填充的命中率
    6. 密钥类型是被禁止直接填入的,所以作为功能性字段,它只能提供签名、提供口令等功能

      如果有输入密钥的功能,请使用“密码”类型进行存储,这需要使用者务必小心,因为通常不会有这种需求,有这种需求通常是流氓软件

  3. 对于一张卡片中需要加密的部分,用户可以勾选那些敏感信息的字段,并为其赋予查阅密码
  4. 为了对抗网络画像,我们会推荐用户使用多个身份来做不同的事情,这也是这个身份存储器能过提供的功能
    1. 比方说,在你购买生活用品时,我们使用身份 A,在你购买电子产品时,使用身份 B,使用不同的身份从而隔绝用户画像的生成问题,这样可以友好地保护用户隐私

      但免不了有些霸王条款会强制用户的物理身份证认证信息,这时候也没有办法,毕竟物理身份证信息的一般是需要进行三方认证的,这时候用户画像跟你的手机号码背后的身份证号码进行强关联,从而无法避免。
      但是随着分布式网络的构成,这种理念在 dweb-browser 中会变成有专门的人代理做中间商,就会你可以借用别人的用户画像购买特定的商品,配合使用数字人民币这类大型私有链,从而避免商家的隐私窃取。
      至于是否使用这种方式去进行一些非法工作,这属于数字人民币的工作职能,它能大数据分析其中的资金流动,从而打击犯罪。另外,也需要用户自觉,尽可能购买使用全链路受法律管辖支付的商品。

    2. 当调用者尝试来读取用户的信息时,我们可以根据应用的“分类”,为其提供推荐的身份,或者用户可以快速在第一身份(默认身份证)的基础上创建出一个新的身份
  5. 要实现“全链路监管”,需要有一个“身份证书”来解决认证问题。

    全链路监管在有些地区是必要的,这是个人与社会的关系中需要去遵循的平衡,毕竟和平不是免费的午餐,需要一个区域中的所有人都遵循某种协议才能达成。
    因此证书的作用,是用来证明身份的合法性,比方说店铺需要有营业资质,个人也需要有身份信息。
    基于这个证书,我们可以实现类似于“身份验证器”的功能,并给予此功能实现隐私保护,比方说 A 向 B 购买商品,那么 A 与 B 需要使用证书来生成一个“临时身份”,这个临时身份是可以被验证“资质合法性”,那么 A 与 B 都可以对于资格进行验证,这时候就不是面相身份了,因此也就能避免信息收集的问题。

【提案】 🎉 `dweb+protocol://tunnel.http.std.dweb`

负责提供 HTTP 网络隧道网络模块,可以将自己的网络暴露到公网中

  1. /registry?subDomain=*&publicKey=*&ttl=* 提供一个公钥,并用此注册一个域名,并且需要提供一个域名存活时间
    1. subDomain 只能是一个单词[a-zA-Z0-9],不可出现./-这类特殊字符,如果出现了预期之外的单词,那么服务器有权拒绝注册,或者会自动给出一个尽可能保留合法内容的词汇,比如说:abc@efg可能会变成abc0efg19
    2. ttl 意味着域名存活时间,意味假设服务器与注册者断开连接后,服务器会为注册者保留多少的。如果在付费服务器上,这项服务可能会根据存活时间来进行收费
    3. 如果有需要,带上受到支持的加密算法名称 &algorithm=*,取决于服务端支持的算法范围
    4. 如果有需要,带上受到授权签名 &auth=*,这是对某一段信息进行加密,加密者为 subDomain 的上级所有者,所以首先要服务端实现多级管理的功能
      1. 如果需要路由的负载均衡,那么 auth 中需要携带的信息是:{ toPublicKey:String, matchs:URLPattern }
      2. 如果需要域名的多级分发,那么 auth 中需要携带的信息是:{ toPublicKey:String, subDomain: String },其中 subDomain 可以是 a.b/*.b/**.b
    5. 200 返回注册成功后公有域名
    6. 401 认证错误,可能是域名所有权被占用
  2. ✨ 在 tunnel.dweb-browser.com.dweb 应用中,它还会读取本地 http 服务器的统一端口,并将这个端口转发到服务端
    1. [get]file://http.sys.dweb/gateway-port 读取当前 http 服务所监听到网关端口
    2. ✨ 应用需要提供一个“一键启动”的管理界面,第一次启动这个 jmm 应用后,需要用户同意才会启动这个隧道服务,这时应用需要用户输入它的用户名,或者唤醒 id-card.std.dweb 来获取

【提案】 🎉 `dweb+protocol://splashscreen.window.std.dweb/`

✨ 这是一个渲染器,一般用于提供启动屏的渲染功能,它提供了类似 css-background-image 的声明式渲染,并且接口的设计上也进行了一定的借鉴
参考资料 Web-CSS-Background

  1. /render?query=*&color=*&image=*&position=*&size=*

    🔊 增加 MicroModuleManifest.mainWindow 字段,可以配置应用启动的时候默认视图的 URI,从而可以将视图委托给第三方模块来渲染,为此可以可以实现将快速将一个 Web 打包成一个 App 的功能

    1. CSS-Background 中,它可以提供多个 background(使用,分隔),正常情况下,这些 background 是叠加在一起的
    2. 这里额外提供了一个 query 字段,参考 container-query,同样我们也使用,分割,并将匹配的 background 进行显示,如果不提供默认为匹配成功
    3. 🔊 我们是否要直接使用 js-disabled 的 webview 进行渲染(我们自己进行 html+css 的翻译),还是用原生实现只提供一个子集?

另外还需要探讨的是,如何将它与 window.std.dweb 进行合理的集成,从协议层面应该怎么设计,从代码层面应该怎么设计,如何将它们有序地进行串联。

考虑更新MicroService.Ipc的设计

namespace DwebBrowser.MicroService
{
public abstract class Ipc{...}
}

这里的一系列SupportsXXX 我的建议是用ISerializable + XXXSerializer来实现 😅

【提案】✨ jmm.browser.dweb

  1. ✨ 实现 dweb://jmm-store?source=jmm-store-manifest.json.url 协议,可以将一个网页注册成“JMM 应用源”,就像传统 RSS 订阅源给到用户的概念一样
    1. ✨ 其中 jmm-store-manifest.json 继承于 common-manifest,同时需要补充一些配置:包括 searchUrl、detailUrl
    2. ✨ 用户在使用 web.browser.sys 访问网页的时候,如果打开 dweb://jmm-store?source=url 链接,那么就会提供一个询问跳转的对话框,同意的话就会替代性地使用 jmm.browser.dweb 访问这个网页
  2. ✨ 实现 JmmStore Controller/.Render,用于访问某个应用源,原理和 web.browser.dweb 类似,差别在于会额外提供一些接口,使得网页可以 读取应用列表、读取安装进度、控制安装 等等。
    1. ✨ 其渲染层和 web.browser.dweb 类似,它也可以打开一些特定的网页(如果),这些网页就是“分布式商店”,就像搜索引擎一样。
    2. ✨ 因为这些网页商店没有后端的程序,而鉴于常规 web-api 的标准,我们需要提供一个 navigator.jmmStore 对象,提供 Promise-Like-Api。(注意,这里不需要走 fetch 这样的网络层接口,也不需要提供这样的接口)
  3. ✨ 实现 JmmStoresMananger Controller/.Render,用于 添加、删除、访问 多个应用源

【提案】 🎉 file.std.dweb

🔊 也许时 cdn 模块的标准?

🔊 是否要增加分组功能?答:不用,专注于自己可读可写就行

这是标准库。要理解 std 和 sys 的主要差异,可以将 sys 理解成直接面向操作系统的 API 进行封装,而 std 属于公共网络服务,任何模块都可以在网络上获取一个存储服务

  1. ✨ fs-api:
    • open 双工操作,关闭时自动close
      • read
      • write
      • size
      • append
    • read
    • write
    • stat
      • 包含 readonly 功能
    • watch
    • mkdir
    • remove
    • list
    • copy
    • move
    • exist
    • permission 获取文件的权限信息,这里的权限以公钥为用户,权限有三种: read/write/manage
  2. ✨ 可扩展性、隔离性

    根据路径来进行扩展,这种扩展是编程角度的,对外它目前没有提供扩展功能

    • /data/ 默认的持久化路径,根据 ipc.mmid 来进行隔离
    • /cache/ 默认的缓存路径,根据 ipc.mmid 来进行隔离
  3. ✨ manage-api
    • key(配置密钥)
    • quotas(配额量)

      通常情况下,我们会使用 key.std.dweb 来主动生成密钥,并导入到 file.std.dweb/env 中,等于让 key.std.dweb 来帮助我们存储密钥,这样就不需要每个应用自己去维护密钥
      同时利用 key.std.dweb 的密钥导入导出功能,我们就可以实现很自然地读取其它设备的数据

【提案】✨ window.std.dweb 实现三层结构

  1. 内容层:包含 Window、Top-Window、Picture-In-Picture 等内容

    目前我们主要使用窗口渲染适配器来实现这一部分内容,这点中期内不会改变
    未来如果有窗口共享,就是来自网络的窗口,比如窗口流转,那么这个代码仍然可以复用,需要在原机器上渲染这个图层的内容,然后通过局域网络传输这个图层,就像 WebRTC 一样。
    目前内容层只提供了“窗口内容渲染”的标准,这在很长一段时间内不会发生改变,对于应用来说,它只直到自己的视图被渲染在某一个物理宽高并且有特定缩放比例的矩形

    1. ✨ 窗口内容会实现“图层管理”功能(和现在的 mwebview 类似)

      “图层管理”除了包含 zyzwh 的属性之外,还会提供基本的 transfrom,从而开发者可以基于这个实现传统应用的路由跳转的动画。

  2. 模态层:通常包含 Bottom-Sheet、Dialog、Side-Sheet
    1. 模态层不像内容层,是可以并发渲染多个内容的,模态层是单发的模式,基于先进先出原则,同一时间只会有一个模态窗口出现,应用每次只能申请一次模态层的渲染权限(和 wid 类似的一个句柄),只有当前模态框被关闭后,才能重新申请。所以鼓励应用尽可能在一个模态框中将需要的交互全部完成
    2. 注意,要理解这里使用“通常”这个词汇,是因为模态层是类似 Window 的一个视图,它也包含了边框:其包含所属应用的基本信息和一些基本的控制按钮。因此它并不局限于上诉的视图形式,所以你可以在这个渲染层中自己去渲染多个 Bottom-Sheet 视图、多个 Dialog 都没关系,而和 Window 最大的区别是,此时你是无法注册返回按钮的。

      注意,模态层在实现的时候,一定会避免被伪造,因为它通常出现在跨应用交互之间

    3. 对于模态层的定义就是要强制用户进行交互,而反过来,用户当然可以强制关闭的模态层,因为我们在多个平台上统一了返回按钮的行为,所以模态层只会有两种配置:一种可以一次返回就关闭模态层,一种需要用户两次返回来关闭模态层
    4. 但是前期,我们只会提供 Bottom-Sheet、Dialog 的标准渲染接口,给到 jmm 应用的接口也是如此。未来可能随着开发者的需求增加,我们才会提供一些比较复杂的渲染组件
  3. 通知层:专门用于绘制 notification、toast、notification-drawer 等

    toast 和 notification 不是用原生的 toast,因为需要显示应用图标和 mmid,但如果用户离开了 dweb-browser 应用程序,那么会使用 notification.sys.dweb 来告知用户后台情况。和聊天应用的图标类似,我们会将应用的 icon、shortname、mmid 显示出来,并将相应的信息显示出来

    1. toast 的作用是程序在后台时(没有 win 或者 win 被最小化),需要像用户提供一些进度的信息时,那么就可以使用 toast 来显示一些信息,这时候会使用 taskbar 来显示 toast:taskbar 会将应用的图标显示出来,并用水平 tooltip 的形式来显示 toast-text。它没有 action,但是它挂在 app-icon 旁边,所以用户可以点击 app-icon 将应用窗口恢复。

      注意,即便有 app 在最大化的模式下,这时候 taskbar 默认只显示当前 app 的 icon。此时如果有 toast,那么对应的 app-icon 还是会动画地显示出来。

    2. notification 一般显示在 window 顶部,如果可以显示在顶部窗口栏上方,如果不行,显示在顶部窗口栏的下方,它可以包含一个 action(使用提供图标(ImageResource)或者文本作为 Action 的内容)。
      1. notification 本身可以包含一个 url,如果有 url,那么会显示一个拖拽栏,用户可以拖拽展开这个 notification.url:将激活成窗口。用 tap 可以达成一样的目的。
      2. notification.action 提供了另外的 url,比如可以用于复制文本,或者打开另外的窗口
      3. 上滑、左滑、右滑都会关闭它

        未来可能会开放额外的接口,来实现自定义配置

实现应用数据导出

实现一键导出应用的所有数据

  1. 当下,生态中的应用基本可以用js脚本做到将前端中ls、indexeddb的数据导出、后端坐到空导出也就够了
  2. 未来,dwebview(remoteMM) 只能打开remoteMM的mmid对应的子域名,mwebview也是如此,想要打开外部的域名,只能回到web.browser.dweb中打开。这是jmm.browser.dweb与web.browser.dweb之间的一个私有特殊行为,目的是保障应用安全。在此基础上,应用的数据管理就会更加简单,我们可以记录用户访问过的所有域名,然后用代理的方式改变这些域名的响应内容,从而帮助用户管理所有数据。(使用 https://developer.android.com/reference/androidx/webkit/Profile 来管理应用数据这个方案不具备通用性,当缺失可以解决一些问题。比如方便我们做接口设计)
  3. 未来,在此基础上,我们在进行应用升级之前或者升级之后,用户可以通过这个功能对自己的数据做管理和保护:比如备份到云端服务器,或者同步到自己的其它设备上

DwebView 网页内部触发的下载应该统一到 download.browser.dweb 中管理

  1. download.browser.dweb 完成下载后,是通过触发 callbackUrl 来执行回调的。
  2. 因此 DwebView 需要提供设置接口,来允许配置 callbackUrl。
  3. Web-Download 是一个不依赖 download.browser.dweb 这个模块的行为,因为有时候服务器需要根据用户的 cookie、session、fetch-body 等请求所携带的信息来进行下载,而不是一个单纯的 get-url 就能解决的,也因此Android、IOS的原生WebView都提供了委托和回调来告知,而具体的下载是WebView程序自己提供的功能。
  4. 也因此 download.browser.dweb 需要提供委托功能,由外部提供下载进度、下载恢复等功能,这些都需要使用 ipc 来实现。
  5. 最后,js-process 如何通过 ipc 设置这个callbackUrl,需要 #40 这个提案完成,将 mwebview 模块需要拆分

Kotlin:提供一种更加通用的data class标准

这种 data class 标准出了能满足序列化反序列化的需求,还要能支持转化成 compose 的 state 和 swiftui 的 state。

目前可以参考jmm.browser、manifest、window.std 这三部分的代码来展开研究

【提案】🎉 模块协议

“模块协议”,参考 Obj-C/Swift 里的 Protocol、Golang 里的 Interface,这将是 microModule 支持 POP(面向过程)的重要一环,这里。它基于 deeplink 的协议进行扩展,如上诉格式:它的 hostname 是一个.dweb结尾的标准 domain。

  1. 模块新增字段 protocols: List<Mmid>,这意味着当前模块可以替代指定的 mmid 来实现对应的功能,从而可以实现模块的替换。理论上这种替换对 dns.std.dweb 模块也是有效的

    这是否会存在被人恶意利用不好说,但 dweb 的标准中不会明确说明有什么限制,所有各个平台可以自己做一些实现,如果顾及到安全性,在某些平台上,可以选择性忽视某些安全性模块或者强制性模块,这完全取决于平台
    在 dweb-browser 这个平台中,我们尽可能探索一种在可视化可控制的环境下,给用户完全的自主选择的权利,因此我们暂时不对此做出任何限制,但是会从底层引擎那里告知用户发生了什么事情。
    这需要我们提供一个依赖图谱、互联图谱 来告知用户所有模块的状况,有必要的情况下,用户可以随时掐断这个图谱中的某一个节点,将它冻结,因此对于开启启动项,我们目前需要谨慎给出“记忆功能”,这可以确保用户可以通过重启来恢复整个系统。

  2. dns 模块需要提供一个选择器,当某个模块可以被其它模块替代的时候,提供选择器
  3. dns 需要记忆该选择,有几种记忆模式:
    1. 保持不变(如果有之前的记忆)
    2. 允许一次
    3. 该选择只对当前应用有效
    4. 该选择为全部应用有效
  4. 当模块升级,或者有新的可替换模块出现的时候,选择器需要重新弹出,无关的模块减少不需要重新弹出选择器
  5. 未来 dns 需要为这个选择器提供管理器,也就是“依赖图谱”

file.std.dweb 的标准化

现在 file 协议是由 file.std.dweb 这个模块提供核心服务,其它模块还能通过适配器的方式额外进行适配,从而来影响一个设备中所有模块对于file:///的内容访问。

这其实会带来很多困惑,因此这里我提出一些基本理念来避免一些根本性的安全问题发生。

PS: 以下内容中 “file” 指 “file 模块”,“模块” 指 “其它模块”

  1. file:///file://file.std.dweb的缩写。

    正如http://file://http.std.dweb/fetch的缩写、ws://file://http.std.dweb/websocket的缩写

  2. 模块是完全独立的,我们应该以一个硬件一个模块的理念去看待模块,因此在我们做开发的时候,不可以认为文件是可以直接在硬件层面进行共享。而是要理解成 file 与模块是私有化的,每个模块背后的 file 模块都是完全独立的

    这也是目前 file 模块提供服务的方式,即便是同一个硬件设备上,模块是不能直接访问到其它模块的文件,但是我们现在通过 picker+realPath 这两接口来实现了直接访问。这其实是一个错误的设计,它打破的原则,是一种妥协,它应该被逐渐废弃。

  3. 如果要做所谓的共享,那么概念上不可以脱离我们的“统一传输层”,并且共享必须是“模块与模块”之间发生,而不能是“模块与 file 与模块”的关系,之所以要遵守这种关系,更 DwebBrowser 的愿景有很大关系,我当然不希望基于之前错误设计写出来的代码在未来标准往前走的时候才发现原来的代码都用不了,因此需要及时作出纠正:

    1. 首先数据的源头来自 file,如果只是转发,虽然正确,但是固然存在大量的资源损耗,所以我们需要在“统一传输层”这里打通直连加速。

    2. 直连加速的意味着在客观的硬件层面,作出一张设备与设备之间的连接网,跟路由表一样,数据传输可以自己在这个底层逻辑中自动进行传输优化。

    3. 目前我们已经尝试过类似的方案:就是 ipcBody 的句柄传输,只不过目前这个方案非常具有针对性,不具备标准化和普世性。

    4. 未来用户将个人与家庭的所有支持 dweb 协议的设备进行互联的时候,“统一传输层”就会更加重要,大家一定要把这个目标纳入 DwebBrowser 这个软件的心脏中,如果失去这个目标,那么 DwebBrowser 本身就只是一个把 webview 和网络协议玩得比较花的软件平台而已,只有将设备互联的理念带进来,它才能真正意义上具备“移动区块链基础设施”这个冠名。

    5. 在统一传输层,我们需要根据各种客观因素打交道,通常来说可以归纳成这三种情况:

      1. file =(all_data_1)=> A =(all_data_2)=> B

        就是转发

      2. file =(headers_1)=> A =(headers_2)=> B; file =(body_data_1)=> B

        只转发头,内容还是直连获取

      3. file =(all_data_1)=> A =(headers_2)=> B; file =(body_data_1)=> B

        file 发送了两次内容数据,这种场景是 A 需要读取数据,但对于给 B 的数据并不修改,所以可以由 file 直接发出两次数据

    6. 对于文件的写入,也是同样的原理,应该直接发生在 A 与 B 两个模块之间,不可以绕过模块直接向 file 进行数据写入,这是不安全的。

    7. 但是如果对于每个模块承担资源访问的编程负担,那么这可能会增加模块访问者的心智负担,因为本来是直接面向 file 来做开发的,现在 file 是对方模块私有的东西,导致现在需要换成向对方模块发送数据,让对方模块自己去做 file 的读写,这会非常冗长。因此我们需要一种标准化的方案来解决这个问题:

      1. 首先对于服务模块(a.server.dweb)来说,它要提供 file 访问:

        class AServer extends MicroModule {
          override async bootstrap(bootstrapContext) {
            /// 注册子协议
            this.protocol("file.std.dweb", async (protocolContext) => {
              // 在收到访问的时候
              protocolContext.onConnect((clientIpc, reason) => {
                // 自己连接到 file,构建出一个 子连接
                const fileSubIpc = this.connect("file.std.dweb").fork(reason);
                /// 简单的双向绑定
                clientIpc.pipe(fileSubIpc); // 等价于 clientIpc.onMessage((msg)=>otherIpc.send(msg))
                fileSubIpc.pipe(clientIpc);
        
                /// 也可以在双向绑定是做一些拦截,从而做一些自定义
                clientIpc.pipe(fileSubIpc, {
                  onRequest(request, toClientIpc) {
                    if (request.pathname === "/some-custom") {
                      // ...
                    } else {
                      return toClientIpc.request(request);
                    }
                  },
                });
              });
            });
          }
        }
      2. 那么对于访问模块(b.client.dweb),它可以这样做使用 sub-protcol 来进行资源访问:

        const serverIpc = await dns.connect("a.server.dweb");
        await serverIpc.request("file://file.std.dweb/info.txt").text(); // 可以缩写成 file:///info.txt

[bug] 下载服务出现崩溃

复原逻辑

点击下载,然后点击下载暂停,然后随便进入一个页面,再返回下载页面,点击下载出现崩溃

Process: info.bagen.dwebbrowser, PID: 30701
       java.lang.NullPointerException: Can't toast on a thread that has not called Looper.prepare()
        at com.android.internal.util.Preconditions.checkNotNull(Preconditions.java:167)
        at android.widget.Toast.getLooper(Toast.java:265)
        at android.widget.Toast.<init>(Toast.java:250)
        at android.widget.Toast.makeText(Toast.java:865)
        at android.widget.Toast.makeText(Toast.java:853)
        at info.bagen.dwebbrowser.service.DwebBrowserService.downloadAndSaveZip(DwebBrowserService.kt:97)
        at info.bagen.dwebbrowser.service.DwebBrowserService$DwebBrowserBinder.invokeDownloadAndSaveZip(DwebBrowserService.kt:70)
        at info.bagen.dwebbrowser.microService.sys.jmm.ui.JmmManagerViewModel$handlerIntent$1.invokeSuspend(JmmManagerViewModel.kt:122)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.internal.LimitedDispatcher.run(LimitedDispatcher.kt:42)
        at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:95)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
        Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@e5e8a24, Dispatchers.IO]

Kotlin:对 Signal 进行升级

Signal的设计来源于JS的EventSource,而JS的EventSource这种设计模式是面向UI做的设计。
它本身的设计出发点,就是事件可丢弃。类似BroadcastChannel

因此将这个设计给到Signal会存在很大的隐患,这点我们已经在 ipc ready 这个点上遇到过了,现在 ipc ready 就是采用轮训的方式一直来建立握手,这是一个很失败的底层设计。

Signal 还有一个挑战,就是它的 listen 是不需要 suspend 介入,只有emit需要,这就意味着一些隐患:一个事件的触发,是它的sender可以决定什么时候结束,而不是receivcer来决定什么时候结束。也就是说receivcer所提供的suspend函数的生命周期不是receivcer自己能决定的,这在很多场景里就是一种错误。

【提案】🎉✨ mwebview.browser.sys = window.std.dweb + webview.sys.dweb

  1. 🎉 新增 webview.sys.dweb 模块

    它目前还算不上 std,因为 std 需要更加强大更加统一的的接口,比如各种请求拦截。而目前各个平台提供的接口都不一样,我们做不到统一,所以只能下方到 sys 这个级别,来做差异化的接口设计。
    在未来的计划中,webview.std.dweb 可能会被设计出来,但它一定也是基于 webview.sys.dweb 来实现的。webview.std.dweb 的实现原理之前有在群里头发过,基本上,就是用 js 拦截托管所有 html、css、js,只是把 webview.sys 作为一个开箱即用的 super-canvas 来使用而已。

  2. ♻️ 未来 mwebview 会被解构,利用 window.std.dweb 的图层管理功能实现所谓的多视图,其中 webview.sys.dweb 就专注于单视图的渲染。

【提案】🎉 `dweb+protocol://search.std.dweb`

提供搜索协议的实现,jmm 与 tunnel 理论上可以提供这个搜索服务,那么我们就可以知道要安装本地模块,或者是可以链接到某个域名

  1. /query?s=* 返回这个标准定义好的类型,比如:{type:'link',data:''}

【提案】🎉 download.browser.dweb

  1. 实现下载管理页面 DownloadController.Render
  2. 每一个下载项目至少包括以下基本信息:编号、链接、文件名、目标文件夹、下载进度、创建时间、下载来源链接、下载来源模块、下载回调链接、内容类型(mime)
    1. 目标文件夹需要基于下载来源模块来定位,比如如果是 game.xxx.com.dweb 这样的 jmm 程序,它的文件夹是独立隔离的,所以相对应的,目标文件夹需要基于对应的模块进行翻译,所以这里需要 file.sys.dweb 模块的介入?需要统一出一种文件寻址方式,其背后的基本理念是所有的模块都可能来自网络。
    2. 下载链接来源能确保我们可以回到对应的页面,比如 jmm 模块的下载,能够回到 jmm 的详情页
  3. 下载接口包括
    1. /create 创建
    2. /start 开始/恢复
    3. /pause 暂停
    4. /cancel 取消下载(释放句柄),记录还在,可以恢复
    5. /watch/progress 进度监控
  4. 下载后的文件需要打开,那么就需要各个模块提供支持的 mime

    后缀很容易冲突,因此提供 mime 是更好的选择,如果么没有 mime,downloadnmm 还会自动根据后缀补充 mime
    也可以强制选择打开方式,罗列出能过打开 mime 的模块
    该功能通过 deeplink 来实现:dweb://open/${mime}?path=${path},这里 mime 使用前缀匹配,比如 image/png的类型可以被 dweb://open/image?path=${path}的模块给处理

【提案】✨ key.std.dweb 密钥管理接口

  1. KeyController/.Render,管理面板,提供导入、查看、导出等基本功能
  2. /{(create|delete)}/?for=:reason 创建密钥,提供某一个原因。会基于 root-key+ipc.mmid+reason 来创建出一个密钥,这个 reason 也是用户在这个密钥时,辅助理解这个密钥的内容

【提案】✨ ipc 协议升级 => ipcPool

详见 desktop-dev/Readme.md

  1. ✨ 增加握手,使用 UTF8 文本进行握手(这是兼容性最广的标准,因为基本现代化所有编程语言一定会支持 UTF8 字符串)、

    需要在握手的过程中解决协议支持的
    可以在握手里协商默认的 IpcBody 行为,现在默认是 Paused 状态,可以协商为默认为 Pulled 的状态,这对网络模块这种一定会进行数据转发的会比较友好

  2. ✨ 加入 cbor 的支持,在原生上会有更好的编码性能和效率,在 js 侧可以省去?
  3. ✨ 加入 createChannel 的支持,用于替代大多数场景下使用 ReadableStreamIPC
    1. ✨ ReadableStreamIpc 需要改变格式(现在时 len+body),避免阻塞(改成 channelId+len+body)
  4. ✨ 新增 IpcError,提供原生的错误支持

【提案】控制窗口内容的渲染方向(横屏、竖屏)

  1. 首先要考虑在桌面端上,它本来就是天然的横屏模式;在传统手持设备上,它的天然是竖屏;在平板设备上,更多是横屏模式少数是竖屏;在折叠屏设备上,更多是竖屏模式少数是近正方形模式;

  2. 其次还有一个点:关于屏幕的方向,用户拥有第一话语权,其次才是应用拥有建议权

  3. 另外,因为多窗口模式的存在,我们不能只针对一个窗口进行旋转,虽然理论上可行,但细想就很难实现:我们需要对触摸的位置进行转换,而 webview 属于系统视图,这通常做不到;还有输入框与软键盘的配合也会出问题。因此需验证必须是一整个屏幕方向都统一进行旋转,而不是特指某一个窗口

  4. 一旦屏幕方向进行了旋转,那么窗口的位置与大小根据以下算法优先级进行:

    1. 保障窗口的内容大小:如果在新的屏幕大小下,内容如果受到了挤压,那么判断如果可以进行宽高交换后可以不受挤压或者说减少挤压,那么进行换高互换(挤压会发生,一个很重要的原因是窗口的存在边框)。
    2. 保障窗口的内容比例:不论宽高交换与否,如果内容受到了挤压(面积缩小),那么我们尽量保持内容比例不变的基础上去渲染窗口
    3. 保障窗口的位置:使用原窗口的重心(一般就是中心)所在 x/y 轴线的比例进行转换,这首先需要知道转换后的窗口外边框大小,然后计算它与整个屏幕的大小差值,在基于这个差值与坐标比例进行相乘,得出的就是新坐标的位置
  5. desktop 与 taskbar 同样需要做自适应设计

    1. 其中 desktop 最好是做“瓷砖设计”:
      1. 以 6✖️6 作为一个基本瓷砖单元来摆放图标与组件
      2. 那么在窗口旋转的时候,多个瓷砖使用流式布局来进行自适应摆放,瓷砖内的内容因为是正方形,所以旋转即可继续使用
      3. 瓷砖内的内容虽然可以旋转,但是瓷砖的位置可以是固定的,如地图一样,因此屏幕旋转后,滚动的方向保持不变(滚动时可以显示 minimap 来引导用户所在位置)
    2. taskbar 作为一个应用抽屉,做好单向滚动与展开就行
      1. taskbar 在默认情况下是一个一维的竖向,因此屏幕方向发生改变的时候, 只需要缩短伸长这一维的滚轴即可
      2. 如果未来 taskbar 需要承载很多应用切换,可以考虑“拟物化设计”:
        1. 设计成类似 picker 的滚轴
        2. 提供一个选中指示器
        3. 用户可以通过滚动来快速切换当前要聚焦的应用
      3. 可以考虑提供一个快速的 resort-toggle 按钮?打开这个按钮,然后依次选中应用图标,这时候应用并不会被聚焦,而是图标会被排序到最前方,这是否可以缓解滚轴缩短导致不易寻找切换应用的问题?
  6. 如果窗口在最大化模式下,在窗口 menuPanel 提供“切换设备屏幕方向”的按钮,默认情况下,该项是“Auto/自动”,意味着跟随系统。

    1. 在折叠设备中,可能会有更加丰富的选项来堆砌进行适配
    2. 目前这个功能值在窗口最大化模式或全屏模式下提供,此时用户可以用这个按钮强制进行方向旋转
    3. Android 中只要声明了可旋转,那么即便方向锁定,但屏幕发生了旋转,那么系统会给出一个小按钮来告知用户可以进行旋转,那时候也可以可以进行强制旋转,不过属于操作系统性质的功能
  7. 最后关于该提案具体的 API,这里给出两种方案,其对应的接口设计和功能设计如下:

    1. 方案一 prefersWindowAspectRatio 提供窗口比例偏好
      1. 新增 file://window.std.dweb/prefersWindowAspectRatio?ratio=W1:H1,W2:H2...
        1. 基本的格式是{width-weight}*{height-weight}
        2. 多个声明使用,进行分隔
        3. width-weight/height-weight 的的格式是:+float && > 0
      2. 在 manifest 中新增属性:prefersWindowAspectRatio:List<width:Float,height:Float>
      3. 在浮动模式下,我们默认选择对当前屏幕的比例最接近的一个比例来展示窗口
    2. 方案二 prefersWindowSize 提供窗口大小偏好
      1. 新增 file://window.std.dweb/prefersWindowSize?size=W1*H1,W2~W2*H2,>=W3*<H3...
        1. 基本的格式是{width-size}*{height-size}
        2. 多个声明使用,进行分隔
        3. width-size/height-size 的的格式是:
          1. dp = +float 正数,缺省了dp这个单位
          2. percent = +float% 正数,不可大于 100%,缺省了vw/vh单位
          3. with_unit = +float 'dp'|'vw'|'vh'|'vmin'|'vmax' 可以携带三种单位:dp 是指物理像素;vw 为百分比屏幕宽度,vh 为百分比屏幕高度,参考 css
          4. base_size = dp | percent | with_unit 最终都会计算成 dp
          5. min_size = 'min(' base_size[, base_size[, base_size...]] ')'
            1. min 函数挑选出多个值中最小的 dp
            2. min 函数一定会注入一个 maxViewWidth/maxViewHeight 值,也就是说怎么都不会超出这个值。一般来说就是屏幕宽高
          6. max_size = 'max(' base_size[, base_size[, base_size...]] ')'
            1. max 函数挑选出多个值中最小的 dp
            2. max 函数一定会注入一个 minViewWidth/minViewHeight 值,也就是说怎么都不会小于这个值。一般来说就是屏幕宽高的 20%
          7. size = base_size | min_size | max_size 代表一个可以算出来的精确值
          8. between_range = size '~' size 代表一个范围,要求两个 size 前者必须要小于等于后者
          9. min_range = size '~' 代表最小值到maxViewWidth/maxViewHeight的范围
          10. max_range = '~' size 代表minViewWidth/minViewHeight到最大值的范围
          11. range = min_range | max_range | between_range
          12. width-size/height-size = range|size最终,宽高的定义,可以是一个具体的值也可以是一个范围
        4. 如果格式解析出错,那么不会发生异常,那么只会提供警告,然后剔除掉这项错误格式的配置项
        5. 默认情况下 prefersWindowSize 的值为0~*0~,意味着可以进行完全自由的适配
        6. 如果用户自己设置的 prefersWindowSize 最终解析下来,没有任何合法的项,那么会缺省使用0~*0~
        7. 虽然可以配置成0dp,但是渲染层会有自己的 WindowLimits 设置,它会将窗口的最小值锁定成:min(80dp, 20%) * min(80dp, 20%)
      2. 在 manifest 中新增属性:prefersWindowSize:List<width:String,height:String>
      3. 在窗口的 menuPanel 中提供“切换窗口尺寸”的按钮,用户可以强制选择自己要的窗口视图,默认情况下,该项是“Auto/自动”,意味着根据算法自动进行匹配(参考深色模式切换功能)
        1. 该算法,基于当前的窗口大小,在配置数组中依次进行寻找,并计算出对应的 scale 值,直到 scale 为 1
        2. 如果没有 scale 为 1,那么使用 scale 最大且稳定排序靠前的项
      4. prefersWindowSize 会自动匹配现有的设备分辨率和方向,自动选择匹配度最高的那个。但要注意,这并不意味着窗口的大小一定会跟着 prefersWindowSize 的设置去走,prefersWindowSize 只是一个参考值,具体窗口的大小,用户是拥有完全的控制权的(在未来的 VR 设备上更是如此)。因此如果有需要,开发需要自己去适配 prefersWindowSize 情况之外大小。

        比方说,开发者可以将自己的内容锁定成 600✖️400 的大小,但是窗口给了 600*450 的大小,那么开发者可以自己将那多出来的 50dp 的高度进行黑边处理

      5. 浮动模式是比较特殊的,它自带 scale 算法:

        目前,我们对浮动窗口简单粗暴地进行了初始化,但如果要实现这个提案,我们最好需要对窗口的大小和位置进行记忆

        1. 浮动模式下,我们
      6. 如果在当前设备屏幕方向下,没有完美的匹配度,那么可能会发生的事情 scale 属性值过小,比如在竖屏模式下显示横屏窗口,那么此时窗口会自动识别设备是否可以修改设备屏幕方向。如果可以,那么 menu 按钮上会浮动出啊一个 tooltip,显示“切换设备屏幕方向”按钮,用户可以快速点击它来实现屏幕方向的切换,这样可以省去打开 menu 面板的步骤。

    使用比例的方案可以给内核开发者更多的适配方案,对于开发者来时心智成本比较低,不过可能需要配合 minWidth/minHeight 来做一些限制
    使用大小的方案理论上可以很精确地控制窗口,但这内核开发者与用户来说,可能会与应用开发者的意图发生冲突

参考资料:
Android
IOS

关于 BFSA 的网络可编程的标准提案

  1. 注册表服务(根域名风格)

    class DwebDNS {
        map: Map<domain, MicroModule>
        query(domain:string):MicroModule?
    }
  2. 通用通讯标准

    class IpcRequest{ 
        method:string
        url:string
        body:string
        headers:Map<string,string>
        onResponse = (response: IpcResponse)=>{}
     }
    class IpcResponse{ 
        request:IpcRequest
        statusCode:number
        body:string
    }
    class Ipc {
        postMessage (request: IpcRequest)
        onMessage = (request: IpcRequest)=>{}
    }
  3. 微组件抽象类

    abstract class MicroModule{
        mmid:string
        abstract bootstrap()
        abstract ipc: Ipc
    }
  4. 原生的微组件

    class NativeMicroModule extends MicroModule{
        mmid: `${string}.${'sys'|'std'}.dweb`
    }
    1. 启动组件
      class BootNMM extends NativeMicroModule{
          mmid: 'boot.sys.dweb'
          bootstrap() {
              // (new MicroModule.from('desktop.sys.dweb') as JsMicroModule).boostrap()
              for(const mmid of registeredMmids){
                  (new MicroModule.from(mmid)).boostrap()
              }
          }
          $Routers:{
              '/register': IO<mmid, boolean>
              '/unregister': IO<mmid, boolean>
          }
      }
    2. GUI组件
      class MultiWebViewNMM extends NativeMicroModule {
          mmid: 'mwebview.sys.dweb'
          bootstrap(args) {
              this.viewTree = new ViewTree();
          }
          $Routers: {
              '/open': IO<WindowOptions, number> & (ctx, args: WindowOptions) => {
                  const webviewNode = viewTree.createNode(Webview, args)
                  viewTree.appendTo(ctx.processId, webviewNode)
                  return webviewNode.id
              }
              '/evalJavascript/(:webview_id)': IO<code, json>
              '/listen/(:webview_id)': IO<void, {request_id}[]>
              '/request/(:request_id)': IO<void, json>
              '/response': IO<Response, void>
          }
      }
      
      const webview_id = await fetch('file://mwebview.sys.dweb/open').number();
      for await (const line of fetch(`file://mwebview.sys.dweb/listen/${webview_id}`).stream('jsonlines')) {
          const request = IpcRequest.from(await fetch(`file://mwebview.sys.dweb/request/${line.request_id}`).json());
      
          const response = IpcResponse(request, {...data});
          await fetch(`file://mwebview.sys.dweb/response`, { body:response });
      }
      
      import { MWebView } from '@bfex/mwebview';
      const webview = await MWebView.create();
      webview.onRequest((event)=>{
          event.responseWith()
      })
  5. 可动态加载的微组件

    class JsMicroModule extends MicroModule{
        // 该程序的来源
        origin: `https://${string}`
        mmid: `${string}.${'bfs'|string}.dweb`
        bootstrap(args){
            /// 我们隐匿地启动单例webview视图,用它来动态创建 WebWorker,来实现 JavascriptContext 的功能
            const ctx = JavascriptContext.create(args.processId);
            ctx.evalJavascript(boostrap_code);// 为这个上下文安装启动代码
            ctx.evalJavascript(args.main_js);// 开始执行开发者自己的代码
        }
        ipc = new JsIpc()
    }
    class JsIpc {
        constructor(jsCtx) {
            const [port1,port2] = jsCtx.createMessageChannel();
            jsCtx.postMessage(port2)
    
            this.port1 = port1;
            port1.onMessage = (message) => this.onMessage(IpcRequest.from(message))
        }
        postMessage (request: IpcRequest) {
            this.port1.postMessage(request.stringify())
        }
        onMessage = (request: IpcRequest)=>{}
    }
    class WasmMicroModule extends MicroModule{
        bootstrap(args){
            /// 我们隐匿地启动单例webview视图,用它来动态创建 WebWorker,来实现 JavascriptContext 的功能
            const ctx = WasmerContext.create(args.processId, system_abi = {ipc_send});
            ctx.run(args.main_wasm);// 开始执行开发者自己的代码
        }
    }

🐛 maxTarget 需要下取整?

因为 target 可能出现 2.1、2.2、2.3 这样的功能性布丁,这都是与 2 兼容的。
这时候 maxTarget 如果写了 2,那么往往就意味着 2.? 其实都是可以的。
因此 minTarget 可能写的是 2.2,maxTarget 写的可能是 2,出现 minTarget 大于 maxTarget 的情况,这会令人疑惑
所以可以考虑这两种写法:

  1. <3 提供版本运算符?但是看着很奇怪,而且增加了学习成本,不建议
  2. 2.* 提供简单的通配符,这个灵活性很多,可能过大了?但个人更加建议这个方案

【提案】♻️ desk.browser.sys

  1. ♻️ desktop.vue、taskbar.vue 使用 Compose 替代实现
  2. ♻️ 重构窗口的启动流程,Application 被启动的时候,窗口会被直接打开渲染(未来会显示一个简易的 TUI 作为默认视图,目前没有,就用启动页),也就是说窗口的激活行为不再属于 plaoc 来管理,而是由 desk 自己内置。那么相应的,程序需要向 desk 模块领取自己的 wid(窗口句柄),然后再将这个句柄发给 mwebview,让它使用这个句柄进行渲染注册。
  3. ✨ 动效强化,所有的用户行为都应该有动画来对用户之后的操作形成直觉
    1. ✨ 应用打开时,需要从 taskbar 中飞出窗口

      注意不是从 desktop 中飞出,一方面我们希望用户接下来的交互都是围绕 taskbar 来展开应用交互;
      另一方面,desktop 未来可能会慢慢进化成一个瀑布流的桌面组件集,我们的目的时让每个应用的最基本的工作都可以在 desktop 上,只有专业工作或者说完整的工作才需要到应用内部进行使用。也就是说 desktop 会慢慢进化成一个大地图,用户像在游戏中的传送门一样快速地找到自己想要的新功能。在这种形态下,地图上大部分是通过算法固定算出来,也就是说在每台设备上都一样;同时会给用户可定制化的个人空间,用来做个性化地传送门功能
      同时 taskbar 会进化成应用抽屉、应用收纳,所有的应用都可以在这里快速找到入口。虽然 taskbar 不想 desktop 一样哟那么大的空间可以做一个大地图,但是 taskbar 仍然可以做一个“颜色地图”,它可以一维化,也可以二维三维,灵活性更高,因为相比于文字可能存在国际化问题、多音、等多种排序标准导致的困惑行,颜色对于人类来来说有着更加统一的效果。具体点说,可以想象一下有人使用很多张照片拼出一张的照片的效果,一张照片虽然颜色很多,但颜色能通过信息丢失进行像素化,随意还是可以简化成一个颜色。

    2. ✨ 应用最小化时,需要将它动画收入到 taskbar 中
    3. ✨ 点击“切换桌面”时,所有窗口收入切换按钮中,按钮层叠显示窗口的数量(不超过 4 个,注意这不是固定的图标,而是动态计算出来的圆角矩形,只是显示上不超过 4 个而已),其中每一个窗口恢复时,动画逆放(也就是说即便是通过点击 taskbar 的图标,它的逆放也是从切换按钮中来的。)
  4. ✨ 打开一个新的窗口,类似 app 双开?
  5. ✨ 新增桌面小组件功能,将 taskbar 移植到桌面组件上

    需要管理节目来管理摆放的顺序

【提案】🎉 permission.sys.dweb

主要是提供一种注册权限的通用标准,它本身没有任何权限功能,它只负责管理权限。也就是说哪些应用申请权限,都由 permission.sys.dweb 提供询问对话框。这可以避免同时多个权限申请时,对话框可以有序进行调度。

  1. [get]/open-native-setting-page 打开原生的授权设置页面
  2. ✨ dweb+protocol://permission.std.dweb 模块协议,用于注册 ipc 通讯时权限
  3. ✨ permission.std.dweb + permission.sys.dweb 使用 bottom-sheet 来展示权限申请对话框
       ┌─────────────────────────────────────┐
       │ ┌─────┬──────────────────┬────────┐ │
       │ │icon │ permission text  │ toggle │ │
       │ └─────┴──────────────────┴────────┘ │
       │ ┌─────┬──────────────────┬────────┐ │
       │ │icon │ permission text  │ toggle │ │
       │ └─────┴──────────────────┴────────┘ │
       │ ┌──────┐  ┌───────┐ ┌────────────┐  │
       │ │cancel│  │confirm│ │ GRANT ALL  │  │
       └─┴──────┴──┴───────┴─┴────────────┴──┘
    
    1. ✨ 一个应用的多条权限申请使用在一个 bottom-sheet 面板中使用 toggle-button 列出
    2. ✨ 某一条权限的申请都需要用户阅读至少 1.5~3 秒(基于权限的危险性)后才能同意,同时视图使用 LazyColumn,要确保 item 被渲染才算被用户读取。
      1. 如果是传感器的权限,那么通常只需要 1.5s
      2. 如果是涉及到用户的地理坐标、读写相机、读写联系人、读写存储,那么需要 2s 以上
      3. 如果设计到用户的个人数据(这通常是来自其它应用,如短信应用、读取相册)这类数据非常直接,甚至涉及到直接的个人隐私、人格画册,需要慎重考虑的,需要 3s

        不过一般情况下,像文件的读取、相册的读取、短信的读取,应用可以通过做 picker,让用户自己选择要被应用读取的内容,从而避免隐私被泄露。

      4. 新的权限申请不能被追加到当前的视图列表中,需要后续再进行组合弹出
    3. 权限模块在使用的时候,需要告知用户应用正在使用的权限功能
  4. ✨ 如果在某个应用在进行权限申请对话框时,有其它应用也在这期间调用了权限申请,那么根据 bottom-sheet 的先进先出原则进行排队,不会有抢占式的问题

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.