大更新

This commit is contained in:
2026-08-08 19:16:58 +08:00
parent d289f8d6e2
commit ae5dc3eb46
178 changed files with 69427 additions and 525 deletions
+97
View File
@@ -547,3 +547,100 @@
justify-content: flex-start;
}
}
.case-summary {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 12px;
margin-top: 18px;
}
.case-summary article {
display: grid;
gap: 6px;
padding: 15px;
border: 1px solid #e2e8f0;
border-radius: 12px;
}
.case-summary article > span,
.doc-case-tabs button span {
color: #2563eb;
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
}
.case-summary small {
color: #64748b;
line-height: 1.5;
}
.lab-grid {
display: grid;
grid-template-columns: minmax(280px, 0.8fr) minmax(0, 1.2fr);
gap: 14px;
margin: 18px 0;
}
.lab-controls {
padding: 17px;
border: 1px solid #dbe4f0;
border-radius: 12px;
background: #fbfdff;
}
.lab-controls h3 {
margin: 0 0 8px;
}
.lab-controls p {
margin: 0 0 14px;
color: #64748b;
line-height: 1.6;
}
.lab-actions {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.lab-controls button,
.explain-box button {
padding: 8px 11px;
border: 1px solid #bfdbfe;
border-radius: 8px;
color: #1d4ed8;
background: #fff;
cursor: pointer;
}
.lab-controls button:disabled,
.explain-box button:disabled {
cursor: default;
opacity: 0.55;
}
.boundary-grid {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 14px;
}
.boundary-card {
min-width: 0;
padding: 15px;
border: 1px solid #e2e8f0;
border-radius: 12px;
background: #fff;
}
.boundary-card h3 {
margin: 0 0 10px;
}
.boundary-card > pre {
margin: 0 0 12px;
padding: 10px;
border-radius: 8px;
background: #0f172a;
color: #e2e8f0;
overflow: auto;
}
.doc-case-tabs button {
display: inline-grid;
gap: 2px;
}
@media (max-width: 980px) {
.case-summary,
.lab-grid,
.boundary-grid {
grid-template-columns: 1fr;
}
}
+138 -57
View File
File diff suppressed because one or more lines are too long
+35
View File
@@ -0,0 +1,35 @@
export type Amis_Schema = Record<string, unknown>;
export interface Composition_Node {
kind: string;
slot?: string;
children?: Composition_Node[];
}
export interface Composition_View {
protocol: string;
protocol_version: number;
kind: string;
name: string;
title?: string;
body: Composition_Node;
}
export interface Slot_Contract {
component_kind: string;
descriptor_api?: string;
view_api?: string;
data_api?: string;
amis_api?: string;
schema: Amis_Schema;
}
export interface Frontend_Manifest {
protocol: string;
protocol_version: number;
view: Composition_View;
slots: Record<string, Slot_Contract>;
}
export interface Ordered_Slot {
name: string;
slot: Slot_Contract;
}
export function collect_slot_names(node: Composition_Node, result?: string[]): string[];
export function ordered_slots(manifest: Frontend_Manifest): Ordered_Slot[];
export function resolve_renderer_kind(component_kind: string, registered: ReadonlySet<string>): string;
+18
View File
@@ -0,0 +1,18 @@
export function collect_slot_names(node, result = []) {
if(node.kind === 'slot' && node.slot) {
result.push(node.slot);
}
for(const child of node.children ?? []) {
collect_slot_names(child, result);
}
return result;
}
export function ordered_slots(manifest) {
return collect_slot_names(manifest.view.body).map(name => ({name, slot: manifest.slots[name]})).filter(entry => Boolean(entry.slot));
}
export function resolve_renderer_kind(component_kind, registered) {
if(!registered.has(component_kind)) {
throw new Error(`未注册的 component_kind: ${component_kind}`);
}
return component_kind;
}
+15
View File
@@ -0,0 +1,15 @@
import assert from 'node:assert/strict';
import {describe, it} from 'node:test';
import {collect_slot_names, ordered_slots, resolve_renderer_kind} from './gallery_protocol.js';
describe('gallery protocol', () => {
it('recursively preserves backend slot order', () => {
assert.deepEqual(collect_slot_names({kind: 'frontend', children: [{kind: 'slot', slot: 'alpha'}, {kind: 'group', children: [{kind: 'slot', slot: 'beta'}]}]}), ['alpha', 'beta']);
});
it('uses the manifest registry instead of business field knowledge', () => {
const manifest = {protocol: 'adminive.composition-manifest', protocol_version: 1, view: {protocol: 'adminive.view', protocol_version: 1, kind: 'composition', name: 'demo', body: {kind: 'frontend', children: [{kind: 'slot', slot: 'one'}, {kind: 'slot', slot: 'two'}]}}, slots: {one: {component_kind: 'amis_schema', schema: {type: 'tpl'}}, two: {component_kind: 'amis_schema', schema: {type: 'form'}}}};
assert.deepEqual(ordered_slots(manifest).map(item => item.name), ['one', 'two']);
});
it('rejects an unregistered component kind', () => {
assert.throws(() => resolve_renderer_kind('vue_component', new Set(['amis_schema'])), /未注册的 component_kind/);
});
});