feat(docs): add koala-chat widget (#2482)

* 🆕 feat(docs): add koala-chat widget

* add v1.10.0 notification
This commit is contained in:
capdiem
2025-07-05 11:37:00 +08:00
committed by GitHub
parent 0c09e5e3df
commit d44dd1ec61
12 changed files with 198 additions and 116 deletions

View File

@@ -57,5 +57,16 @@
"href": "/blazor/getting-started/release-notes?v=v1.9.0",
"upgradeGuide": "/blazor/getting-started/upgrade-guide#upgrading-from-v1-8-x-to-v1-9-0",
"createdAt": "2025-04-07"
},
{
"title": "v1.10.0",
"content": {
"en": "Highlights:\r\n- [Theming](/blazor/features/theme#specification) System Upgrade: To support multiple themes, the theming system has been adapted to the core color specification of Material Design 3 (MD3) and now fully utilizes CSS variables. This simplifies the process of theme customization and switching. Additionally, the default theme color has been updated to align with brand visuals, and a \"memorial mode\" toggle has been added.\r\n- **Dependency Optimization**: To reduce the main library size, mobile-only components, as well as components like Swiper, Gridstack, MarkdownIt, SyntaxHighlight, and Xgplayer, have been migrated to separate packages.\r\n- **PageStack Feature Enhancement**: Optimized the UI transition animations for page stack push and pop operations. Added the [PageStackTab](/blazor/mobiles/page-stack#ppagestacktab) component to improve tab navigation behavior, with added refresh and badge functionalities.\r\n- [Combobox](/blazor/components/combobox): An Autocomplete-like component that allows users to input and submit custom content.\r\n- [EmptyState](/blazor/components/empty-states): Used to display scenarios of an empty list or no search results.",
"zh": "亮点:\r\n- [主题](/blazor/features/theme#specification)系统升级为支持多主题主题系统已适配Material Design 3 (MD3) 的核心颜色规范并全面采用CSS变量。此举简化了主题定制与切换流程。此外默认主题色已更新以符合品牌视觉并新增了“追悼模式”切换功能。\r\n- **依赖项优化**为减小主库体积仅用于移动端的组件以及Swiper、Gridstack、MarkdownIt、SyntaxHighlight、Xgplayer等组件均已迁移至独立的包。\r\n- **PageStack功能增强**优化了页面堆栈的出入栈UI过渡动画。新增[PageStackTab组件](/blazor/mobiles/page-stack#ppagestacktab),用于改善标签页的导航行为,并添加了刷新和徽章功能。\r\n- [Combobox](/blazor/components/combobox)一个功能类似于Autocomplete的组件允许用户输入并提交自定义内容。\r\n- [EmptyState](/blazor/components/empty-states):用于展示列表空状态或搜索无结果的场景。"
},
"type": 0,
"href": "/blazor/getting-started/release-notes?v=v1.10.0",
"upgradeGuide": "/blazor/getting-started/upgrade-guide#upgrading-from-v1-9-x-to-v1-10-0",
"createdAt": "2025-07-07"
}
]

View File

@@ -113,66 +113,68 @@ The `a` tag has an unavoidable problem: continuous clicks will cause multiple tr
### PPageStackTab {released-on=v1.10.0}
**PPageStackTab** component provides two features:
**PPageStackTab** component provides three features:
- The default `a` tag or component navigation behavior adds a record to the browser history, which increases the history when switching tabs.
In a MAUI Blazor Hybrid app, it is challenging to implement the back button on the tab page to exit the application.
Wrapping the `a` tag or component with the **PPageStackTab** component changes the default navigation behavior to replace the current page with the `replace` method, thus not increasing the history.
1. The default `a` tag or component navigation behavior adds a record to the browser history, which increases the history when switching tabs.
In a MAUI Blazor Hybrid app, it is challenging to implement the back button on the tab page to exit the application.
Wrapping the `a` tag or component with the **PPageStackTab** component changes the default navigation behavior to replace the current page with the `replace` method, thus not increasing the history.
```razor PageStackLayout.razor
@using Masa.Blazor.Presets.PageStack
<PPageStackTab href="/tab-1" TabRule="@TabRule1">
<MButton @attributes="@context.Attrs">Stack page</MButton>
</PPageStackTab>
@code {
internal static TabRule TabRule1 = new("/tab-1$");
}
```
```razor PageStackLayout.razor
@using Masa.Blazor.Presets.PageStack
<PPageStackTab href="/tab-1" TabRule="@TabRule1">
<MButton @attributes="@context.Attrs">Stack page</MButton>
</PPageStackTab>
@code {
internal static TabRule TabRule1 = new("/tab-1$");
}
```
- When the activated tab is clicked again, it will trigger the `TabRefreshRequested` event. When used with the [Pull to refresh component](/blazor/mobiles/pull-refresh), it can achieve a pull-to-refresh effect.
2. When the activated tab is clicked again, it will trigger the `TabRefreshRequested` event. When used with the [Pull to refresh component](/blazor/mobiles/pull-refresh), it can achieve a pull-to-refresh effect.
```razor Tab1.razor
@using Masa.Blazor.Presets.PageStack.NavController
@inject PageStackNavController NavController
@implements IDisposable
<MPullRefresh @ref="_pullRefresh" OnRefresh="...">
...
</MPullRefresh>
@code {
private MPullRefresh? _pullRefresh;
protected override void OnInitialized()
{
base.OnInitialized();
NavController.TabRefreshRequested += NavControllerOnTabRefreshRequested;
}
private async void NavControllerOnTabRefreshRequested(object? sender, PageStackTabRefreshRequestedEventArgs e)
{
if (_pullRefresh is null)
{
return;
}
if (e.TargetTab != PageStackLayout.TabRule1)
{
return;
}
await _pullRefresh.SimulateRefreshAsync();
}
public void Dispose()
{
NavController.TabRefreshRequested -= NavControllerOnTabRefreshRequested;
}
}
```
```razor Tab1.razor
@using Masa.Blazor.Presets.PageStack.NavController
@inject PageStackNavController NavController
@implements IDisposable
<MPullRefresh @ref="_pullRefresh" OnRefresh="...">
...
</MPullRefresh>
@code {
private MPullRefresh? _pullRefresh;
protected override void OnInitialized()
{
base.OnInitialized();
NavController.TabRefreshRequested += NavControllerOnTabRefreshRequested;
}
private async void NavControllerOnTabRefreshRequested(object? sender, PageStackTabRefreshRequestedEventArgs e)
{
if (_pullRefresh is null)
{
return;
}
if (e.TargetTab != PageStackLayout.TabRule1)
{
return;
}
await _pullRefresh.SimulateRefreshAsync();
}
public void Dispose()
{
NavController.TabRefreshRequested -= NavControllerOnTabRefreshRequested;
}
}
```
3. The `InitialBadge` property can set the initial badge, usually used for new message notifications.
The injected **PageStackNavController** provides `RequestTabBadgeUpdate` and `RequestTabBadgeClear` events to update or clear the badge.
### PStackPageBar {released-on=v1.10.0}

View File

@@ -111,66 +111,68 @@ builder.Services
### PPageStackTab {released-on=v1.10.0}
**PPageStackTab** 组件提供个功能:
**PPageStackTab** 组件提供个功能:
- 默认的 `a` 标签或组件的导航行为会往浏览器历史记录中添加一条记录,标签页切换时会导致历史记录的增加。
在 MAUI Blazor Hybrid app 上想实现在标签页上点击返回按钮退出应用程序就比较麻烦。
使用 **PPageStackTab** 组件包裹 `a` 标签或组件,会将默认导航行为改为 `replace` 方法替换当前页面,从而不会增加历史记录。
1. 默认的 `a` 标签或组件的导航行为会往浏览器历史记录中添加一条记录,标签页切换时会导致历史记录的增加。
在 MAUI Blazor Hybrid app 上想实现在标签页上点击返回按钮退出应用程序就比较麻烦。
使用 **PPageStackTab** 组件包裹 `a` 标签或组件,会将默认导航行为改为 `replace` 方法替换当前页面,从而不会增加历史记录。
```razor PageStackLayout.razor
@using Masa.Blazor.Presets.PageStack
<PPageStackTab href="/tab-1" TabRule="@TabRule1">
<MButton @attributes="@context.Attrs">Stack page</MButton>
</PPageStackTab>
@code {
internal static TabRule TabRule1 = new("/tab-1$");
}
```
```razor PageStackLayout.razor
@using Masa.Blazor.Presets.PageStack
<PPageStackTab href="/tab-1" TabRule="@TabRule1">
<MButton @attributes="@context.Attrs">Stack page</MButton>
</PPageStackTab>
@code {
internal static TabRule TabRule1 = new("/tab-1$");
}
```
- 在激活标签页下再次点击导航链接时,会触发 `TabRefreshRequested` 事件,配合 [下拉刷新组件](/blazor/mobiles/pull-refresh) 使用,可以实现下拉刷新的效果。
2. 在激活标签页下再次点击导航链接时,会触发 `TabRefreshRequested` 事件,配合 [下拉刷新组件](/blazor/mobiles/pull-refresh) 使用,可以实现下拉刷新的效果。
```razor Tab1.razor
@using Masa.Blazor.Presets.PageStack.NavController
@inject PageStackNavController NavController
@implements IDisposable
<MPullRefresh @ref="_pullRefresh" OnRefresh="...">
...
</MPullRefresh>
@code {
private MPullRefresh? _pullRefresh;
protected override void OnInitialized()
{
base.OnInitialized();
NavController.TabRefreshRequested += NavControllerOnTabRefreshRequested;
}
private async void NavControllerOnTabRefreshRequested(object? sender, PageStackTabRefreshRequestedEventArgs e)
{
if (_pullRefresh is null)
{
return;
}
if (e.TargetTab != PageStackLayout.TabRule1)
{
return;
}
await _pullRefresh.SimulateRefreshAsync();
}
public void Dispose()
{
NavController.TabRefreshRequested -= NavControllerOnTabRefreshRequested;
}
}
```
```razor Tab1.razor
@using Masa.Blazor.Presets.PageStack.NavController
@inject PageStackNavController NavController
@implements IDisposable
<MPullRefresh @ref="_pullRefresh" OnRefresh="...">
...
</MPullRefresh>
@code {
private MPullRefresh? _pullRefresh;
protected override void OnInitialized()
{
base.OnInitialized();
NavController.TabRefreshRequested += NavControllerOnTabRefreshRequested;
}
private async void NavControllerOnTabRefreshRequested(object? sender, PageStackTabRefreshRequestedEventArgs e)
{
if (_pullRefresh is null)
{
return;
}
if (e.TargetTab != PageStackLayout.TabRule1)
{
return;
}
await _pullRefresh.SimulateRefreshAsync();
}
public void Dispose()
{
NavController.TabRefreshRequested -= NavControllerOnTabRefreshRequested;
}
}
```
3. 通过 `InitialBadge` 属性可以设置初始的徽章,通常用于新消息通知。
通过注入的 **PageStackNavController** 的 `RequestTabBadgeUpdate` 和 `RequestTabBadgeClear` 事件可以更新或清除徽章。
### PStackPageBar {released-on=v1.10.0}

View File

@@ -32,7 +32,7 @@ public class AppService
if (project == "blazor")
{
list.Add(new("getting-started", "/blazor/getting-started/installation", "/blazor/getting-started"));
list.Add(new("ui-components", "/blazor/components/all", "/blazor/components"));
list.Add(new("ui-components", "/blazor/components/application", "/blazor/[components|mobiles]"));
}
list.Add(new("annual-service", "/annual-service", "pricing", "red"));

View File

@@ -78,6 +78,21 @@
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/loader.js"></script>
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.nls.js"></script>
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.js"></script>
<script src="https://opendeep.wiki/koala-chat-widget.js"></script>
<script>
KoalaChatWidget.init({
appId: 'app_mcn5ubhc_z8w4uj',
title: 'masa-blazor-chat',
theme: 'light', // 或 'dark'
// 其他可选配置...
onError: (error) => {
console.error('Chat widget error:', error);
},
onValidationFailed: (domain) => {
console.error('Domain validation failed:', domain);
}
});
</script>
<script>
Blazor.start().then(function () {
Quill.register('modules/blotFormatter', QuillBlotFormatter.default);

View File

@@ -17,6 +17,7 @@ public partial class BaseLayout
internal Action? OnAppBarNavIconClick { get; set; }
internal Config? Config { get; set; }
internal string? Project => _project;
protected override void OnInitialized()
{

View File

@@ -2,6 +2,7 @@
@inherits LayoutComponentBase
@implements IAsyncDisposable
@using Masa.Blazor.Extensions
@inject I18n I18n
<NavDrawer @bind-Value="_showDrawer"
RTL="MasaBlazor.RTL"
@@ -19,14 +20,29 @@
ActiveItem="@context.ActiveTarget"/>
</MScrollToTarget>
<MButton Show="@(BaseLayout.Project == "blazor")"
Large
Fab
Fixed
Bottom
Right
Class="transition-swing"
Color="accent"
OnClick="@OpenAIAssistantAsync">
<MIcon>mdi-assistant</MIcon>
<MTooltip Activator="parent" Left>
<ChildContent>@I18n.T("ai-assistant")</ChildContent>
</MTooltip>
</MButton>
<FabTransition>
<MButton Show="_showBackTop"
Large
Fab
Fixed
Bottom
Right
Class="transition-swing"
Style="bottom: 96px"
Color="primary"
OnClick="@ToTopAsync">
<MIcon>mdi-chevron-up</MIcon>
@@ -84,6 +100,11 @@
await Js.InvokeVoidAsync("backTop");
}
private async Task OpenAIAssistantAsync()
{
await Js.InvokeVoidAsync("openAIAssistant");
}
public async ValueTask DisposeAsync()
{
try

View File

@@ -641,4 +641,8 @@ app-alert p {
.theme--light .typed-list-header {
background-color: rgba(var(--m-theme-surface));
color: rgba(var(--m-theme-on-surface));
}
.koala-floating-button {
display: none !important;
}

View File

@@ -126,3 +126,12 @@ window.addDocSearch = function (index, currentLanguage, placeholder) {
window.isDarkPreferColor = function() {
return window.matchMedia('(prefers-color-scheme: dark)').matches
}
window.openAIAssistant = function() {
const el = document.querySelector(".koala-floating-button");
if (el) {
el.click();
} else {
console.warn("Koala floating button not found.");
}
}

View File

@@ -566,6 +566,7 @@
"misc": "Miscellaneous"
},
"nav-component-group-by-type": "Grouped by type",
"nav-component-group-by-type-desc": "Group the components in the left navigation by type"
"nav-component-group-by-type-desc": "Group the components in the left navigation by type",
"ai-assistant": "AI Assistant"
}

View File

@@ -655,5 +655,6 @@
"misc": "其他"
},
"nav-component-group-by-type": "按类型分组",
"nav-component-group-by-type-desc": "左侧导航的组件列表按类型分组"
"nav-component-group-by-type-desc": "左侧导航的组件列表按类型分组",
"ai-assistant": "AI 助手"
}

View File

@@ -145,6 +145,21 @@
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/loader.js"></script>
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.nls.js"></script>
<script src="https://cdn.masastack.com/npm/monaco-editor/0.34.1/min/vs/editor/editor.main.js"></script>
<script src="https://opendeep.wiki/koala-chat-widget.js"></script>
<script>
KoalaChatWidget.init({
appId: 'app_mcn5ubhc_z8w4uj',
title: 'masa-blazor-chat',
theme: 'light', // 或 'dark'
// 其他可选配置...
onError: (error) => {
console.error('Chat widget error:', error);
},
onValidationFailed: (domain) => {
console.error('Domain validation failed:', domain);
}
});
</script>
<script type="module">
import { BrotliDecode } from './js/decode.min.js';
Blazor.start({