@web-ppt/editor 0.5.0-beta.1 → 0.5.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/CHANGELOG.md +188 -0
  2. package/README.md +97 -6
  3. package/dist/accessibility.js +60 -0
  4. package/dist/adjustments/index.d.ts +2 -0
  5. package/dist/adjustments/preset-adjustment-editor.d.ts +4 -0
  6. package/dist/adjustments/types.d.ts +22 -0
  7. package/dist/adjustments.js +300 -0
  8. package/dist/canvas-accessibility.d.ts +6 -0
  9. package/dist/chart/index.d.ts +2 -0
  10. package/dist/chart-shared/index.d.ts +2 -0
  11. package/dist/chart-shared.js +7 -0
  12. package/dist/chart.js +6 -0
  13. package/dist/common-object-slide-commands.d.ts +20 -0
  14. package/dist/design/index.d.ts +3 -0
  15. package/dist/design-editor-types.d.ts +15 -0
  16. package/dist/design-editor.d.ts +3 -0
  17. package/dist/design.js +108 -0
  18. package/dist/dom-identity.d.ts +4 -1
  19. package/dist/dom-patch.d.ts +1 -1
  20. package/dist/edit-context/geometry.d.ts +8 -0
  21. package/dist/edit-context/index.d.ts +8 -0
  22. package/dist/edit-context/native.d.ts +33 -0
  23. package/dist/edit-context.js +333 -0
  24. package/dist/editor-keyboard.d.ts +4 -1
  25. package/dist/editor.js +1 -6332
  26. package/dist/element-clipboard.d.ts +4 -2
  27. package/dist/framework-adapter-types.d.ts +17 -2
  28. package/dist/image-insertion.d.ts +4 -2
  29. package/dist/index.d.ts +6 -5
  30. package/dist/keyboard-context.d.ts +1 -0
  31. package/dist/keyboard-document.d.ts +8 -0
  32. package/dist/keyboard-group.d.ts +7 -0
  33. package/dist/media/index.d.ts +1 -0
  34. package/dist/media.js +1 -0
  35. package/dist/rotation-geometry.d.ts +4 -1
  36. package/dist/selection-all.d.ts +3 -0
  37. package/dist/selection-hit.d.ts +6 -0
  38. package/dist/selection-pane-types.d.ts +1 -0
  39. package/dist/session-state.d.ts +5 -1
  40. package/dist/session.d.ts +17 -1
  41. package/dist/slide-dom-renderer.d.ts +1 -0
  42. package/dist/slide-editor-commands.d.ts +14 -2
  43. package/dist/slide-editor-events.d.ts +1 -0
  44. package/dist/slide-editor-types.d.ts +37 -2
  45. package/dist/slide-pointer-controller.d.ts +3 -0
  46. package/dist/templates/index.d.ts +2 -0
  47. package/dist/templates.js +5 -0
  48. package/dist/text-edit-commit.d.ts +8 -0
  49. package/dist/text-editor-types.d.ts +2 -0
  50. package/dist/text-editor.d.ts +8 -3
  51. package/dist/text-font-step.d.ts +5 -0
  52. package/dist/text-keyboard.d.ts +28 -0
  53. package/dist/text-list-level.d.ts +4 -0
  54. package/dist/touch-gesture.d.ts +42 -0
  55. package/dist/transform-frame.d.ts +11 -6
  56. package/dist/vertex/index.d.ts +2 -0
  57. package/dist/vertex/types.d.ts +21 -0
  58. package/dist/vertex/vertex-editor.d.ts +4 -0
  59. package/dist/vertex.js +338 -0
  60. package/package.json +42 -6
@@ -0,0 +1,21 @@
1
+ import type { CustomGeometry } from '@web-ppt/core';
2
+ import type { ElementId } from '@web-ppt/edit-core';
3
+ import type { EditorSession } from '../session';
4
+ import type { SlideEditor } from '../slide-editor-types';
5
+ export interface VertexEditorOptions {
6
+ onError?: (error: unknown) => void;
7
+ }
8
+ export interface VertexEditor {
9
+ readonly elementId: ElementId | null;
10
+ readonly geometry: CustomGeometry | null;
11
+ readonly destroyed: boolean;
12
+ /** 省略 id 时只接受当前单元素选区;预设形状必须先显式 convert。 */
13
+ start(id?: ElementId): boolean;
14
+ end(): void;
15
+ convert(id?: ElementId): boolean;
16
+ setClosed(pathId: string, closed: boolean): void;
17
+ setSegmentType(pathId: string, commandId: string, type: 'line' | 'cubic'): void;
18
+ refresh(): void;
19
+ destroy(): void;
20
+ }
21
+ export type VertexEditorFactory = (session: EditorSession, view: SlideEditor, options?: VertexEditorOptions) => VertexEditor;
@@ -0,0 +1,4 @@
1
+ import type { EditorSession } from '../session';
2
+ import type { SlideEditor } from '../slide-editor-types';
3
+ import type { VertexEditor, VertexEditorOptions } from './types';
4
+ export declare function createVertexEditor(session: EditorSession, view: SlideEditor, options?: VertexEditorOptions): VertexEditor;
package/dist/vertex.js ADDED
@@ -0,0 +1,338 @@
1
+ import { queryElementCustomGeometry as f, slideOfElement as v, setCustomGeometryClosed as E, setCustomGeometrySegmentType as S, elementFrameToSlidePoint as b, screenToSlidePoint as A, slideToElementFramePoint as x, moveCustomGeometryPoint as D } from "@web-ppt/edit-core";
2
+ const I = 3, m = (u) => u.pointerId ?? 0;
3
+ class K {
4
+ constructor(t) {
5
+ this.active = null, this.root = t;
6
+ }
7
+ get isActive() {
8
+ return this.active !== null;
9
+ }
10
+ begin(t, e) {
11
+ var i, o;
12
+ this.cancel();
13
+ const r = { x: t.clientX, y: t.clientY };
14
+ this.active = {
15
+ pointerId: m(t),
16
+ startScreen: r,
17
+ snapshot: {
18
+ screen: r,
19
+ altKey: t.altKey,
20
+ ctrlKey: t.ctrlKey,
21
+ metaKey: t.metaKey,
22
+ shiftKey: t.shiftKey
23
+ },
24
+ gesture: e,
25
+ started: !1,
26
+ frame: null,
27
+ cursor: this.root.style.cursor
28
+ };
29
+ try {
30
+ (o = (i = this.root).setPointerCapture) == null || o.call(i, m(t));
31
+ } catch {
32
+ }
33
+ }
34
+ move(t) {
35
+ var r, i;
36
+ const e = this.active;
37
+ if (!(!e || m(t) !== e.pointerId)) {
38
+ this.updateSnapshot(e, t);
39
+ try {
40
+ (i = (r = e.gesture).observePointer) == null || i.call(r, e.snapshot);
41
+ } catch (o) {
42
+ throw this.clear(e), o;
43
+ }
44
+ if (!e.started) {
45
+ if (Math.hypot(
46
+ e.snapshot.screen.x - e.startScreen.x,
47
+ e.snapshot.screen.y - e.startScreen.y
48
+ ) < I) return;
49
+ e.started = !0;
50
+ try {
51
+ e.gesture.start();
52
+ } catch (h) {
53
+ throw this.clear(e), h;
54
+ }
55
+ this.root.style.cursor = e.gesture.cursor, this.root.dataset[e.gesture.dataset.name] = e.gesture.dataset.value;
56
+ }
57
+ t.preventDefault(), this.scheduleFrame(e);
58
+ }
59
+ }
60
+ finish(t) {
61
+ var i, o;
62
+ const e = this.active;
63
+ if (!e || m(t) !== e.pointerId) return "ignored";
64
+ this.updateSnapshot(e, t);
65
+ let r;
66
+ try {
67
+ (o = (i = e.gesture).observePointer) == null || o.call(i, e.snapshot), r = e.started ? e.gesture.finish(e.snapshot) : null;
68
+ } catch (h) {
69
+ throw this.clear(e), h;
70
+ }
71
+ return this.clear(e), r == null || r(), r && t.preventDefault(), e.started ? "gesture" : "click";
72
+ }
73
+ /** 键盘确认使用最后一次指针快照;先清理 capture/rAF,再执行提交,避免订阅回调重入活动手势。 */
74
+ commit() {
75
+ const t = this.active;
76
+ if (!t) return !1;
77
+ let e = null;
78
+ try {
79
+ e = t.started ? t.gesture.finish(t.snapshot) : null;
80
+ } catch (r) {
81
+ throw this.clear(t), r;
82
+ }
83
+ return this.clear(t), e == null || e(), t.started;
84
+ }
85
+ modifier(t) {
86
+ const e = this.active;
87
+ return !e || !["Shift", "Alt", "Control", "Meta"].includes(t.key) ? !1 : (e.snapshot = {
88
+ ...e.snapshot,
89
+ altKey: t.altKey,
90
+ ctrlKey: t.ctrlKey,
91
+ metaKey: t.metaKey,
92
+ shiftKey: t.shiftKey
93
+ }, e.started && this.scheduleFrame(e), !0);
94
+ }
95
+ cancel() {
96
+ this.active && this.clear(this.active);
97
+ }
98
+ cancelPointer(t) {
99
+ this.active && m(t) === this.active.pointerId && this.clear(this.active);
100
+ }
101
+ updateSnapshot(t, e) {
102
+ t.snapshot = {
103
+ screen: { x: e.clientX, y: e.clientY },
104
+ altKey: e.altKey,
105
+ ctrlKey: e.ctrlKey,
106
+ metaKey: e.metaKey,
107
+ shiftKey: e.shiftKey
108
+ };
109
+ }
110
+ scheduleFrame(t) {
111
+ if (t.frame !== null) return;
112
+ const e = this.root.ownerDocument.defaultView;
113
+ if (!(e != null && e.requestAnimationFrame)) {
114
+ try {
115
+ t.gesture.frame(t.snapshot);
116
+ } catch (r) {
117
+ throw this.clear(t), r;
118
+ }
119
+ return;
120
+ }
121
+ t.frame = e.requestAnimationFrame(() => {
122
+ if (t.frame = null, this.active === t)
123
+ try {
124
+ t.gesture.frame(t.snapshot);
125
+ } catch (r) {
126
+ throw this.clear(t), r;
127
+ }
128
+ });
129
+ }
130
+ clear(t) {
131
+ var r, i;
132
+ this.active = null;
133
+ const e = this.root.ownerDocument.defaultView;
134
+ t.frame !== null && (e != null && e.cancelAnimationFrame) && e.cancelAnimationFrame(t.frame);
135
+ try {
136
+ t.started && t.gesture.clear();
137
+ } finally {
138
+ this.root.style.cursor = t.cursor, delete this.root.dataset[t.gesture.dataset.name];
139
+ try {
140
+ (i = (r = this.root).hasPointerCapture) != null && i.call(r, t.pointerId) && this.root.releasePointerCapture(t.pointerId);
141
+ } catch {
142
+ }
143
+ }
144
+ }
145
+ }
146
+ const P = "http://www.w3.org/2000/svg";
147
+ function g(u, t) {
148
+ return u.createElementNS(P, t);
149
+ }
150
+ class C {
151
+ constructor(t, e, r) {
152
+ var l;
153
+ if (this.session = t, this.view = e, this.options = r, this.activeId = null, this.preview = null, this.isDestroyed = !1, this.pointerDown = (s) => {
154
+ const c = s.target instanceof Element ? s.target.closest("[data-ppt-vertex-point]") : null;
155
+ if (!(!c || s.button !== 0 || s.isPrimary === !1))
156
+ try {
157
+ const n = c.dataset.pptVertexPoint;
158
+ this.lifecycle.begin(s, this.gesture(n, this.requireGeometry())), this.stop(s);
159
+ } catch (n) {
160
+ this.report(n);
161
+ }
162
+ }, this.pointerMove = (s) => {
163
+ if (this.lifecycle.isActive)
164
+ try {
165
+ this.lifecycle.move(s), this.stop(s);
166
+ } catch (c) {
167
+ this.report(c);
168
+ }
169
+ }, this.pointerUp = (s) => {
170
+ if (this.lifecycle.isActive)
171
+ try {
172
+ this.lifecycle.finish(s), this.stop(s);
173
+ } catch (c) {
174
+ this.report(c);
175
+ }
176
+ }, this.pointerCancel = (s) => {
177
+ this.lifecycle.isActive && (this.lifecycle.cancelPointer(s), this.stop(s));
178
+ }, this.keyDown = (s) => {
179
+ s.key !== "Escape" || !this.lifecycle.isActive || (this.lifecycle.cancel(), this.stop(s));
180
+ }, t.disposed || e.destroyed) throw new Error("不能挂载到已释放的编辑视图");
181
+ this.root = e.element;
182
+ const i = this.root.querySelector("[data-ppt-stage]"), o = this.root.querySelector('[data-ppt-layer="interaction"]'), h = this.root.querySelector('[data-ppt-layer="static"]');
183
+ if (!i || !o || !h) throw new Error("编辑视图缺少三层 DOM");
184
+ this.stage = i, this.layer = o, this.lifecycle = new K(this.root), this.root.addEventListener("pointerdown", this.pointerDown, !0), this.root.addEventListener("pointermove", this.pointerMove, !0), this.root.addEventListener("pointerup", this.pointerUp, !0), this.root.addEventListener("pointercancel", this.pointerCancel, !0), this.root.addEventListener("keydown", this.keyDown, !0), this.unsubscribe = t.editor.subscribe(() => this.render());
185
+ const y = ((l = this.root.ownerDocument.defaultView) == null ? void 0 : l.MutationObserver) ?? MutationObserver;
186
+ this.observer = new y(() => this.render()), this.observer.observe(h, { childList: !0 });
187
+ }
188
+ get elementId() {
189
+ return this.activeId;
190
+ }
191
+ get geometry() {
192
+ return this.activeId ? f(this.session.editor.doc, this.activeId) : null;
193
+ }
194
+ get destroyed() {
195
+ return this.isDestroyed;
196
+ }
197
+ start(t) {
198
+ this.assertAlive();
199
+ const e = this.session.editor.selection, r = t ?? (e.kind === "elements" && e.ids.length === 1 ? e.ids[0] : null);
200
+ return !r || this.view.mode !== "edit" || v(this.session.editor.doc, r) !== this.view.slideId || !f(this.session.editor.doc, r) ? !1 : (this.activeId = r, this.preview = null, this.render(), !0);
201
+ }
202
+ end() {
203
+ var t;
204
+ this.lifecycle.cancel(), this.activeId = null, this.preview = null, (t = this.group()) == null || t.remove();
205
+ }
206
+ convert(t) {
207
+ this.assertAlive();
208
+ const e = this.session.editor.selection, r = t ?? (e.kind === "elements" && e.ids.length === 1 ? e.ids[0] : null);
209
+ if (!r || this.view.mode !== "edit") return !1;
210
+ try {
211
+ return this.session.editor.exec({ type: "ConvertToCustomGeometry", id: r }), this.start(r);
212
+ } catch (i) {
213
+ return this.report(i), !1;
214
+ }
215
+ }
216
+ setClosed(t, e) {
217
+ const r = this.requireGeometry();
218
+ this.session.editor.exec({
219
+ type: "SetGeometry",
220
+ id: this.activeId,
221
+ geometry: E(r, t, e)
222
+ });
223
+ }
224
+ setSegmentType(t, e, r) {
225
+ const i = this.requireGeometry();
226
+ this.session.editor.exec({
227
+ type: "SetGeometry",
228
+ id: this.activeId,
229
+ geometry: S(i, t, e, r)
230
+ });
231
+ }
232
+ refresh() {
233
+ this.assertAlive(), this.render();
234
+ }
235
+ destroy() {
236
+ var t;
237
+ this.isDestroyed || (this.isDestroyed = !0, this.lifecycle.cancel(), this.unsubscribe(), this.observer.disconnect(), this.root.removeEventListener("pointerdown", this.pointerDown, !0), this.root.removeEventListener("pointermove", this.pointerMove, !0), this.root.removeEventListener("pointerup", this.pointerUp, !0), this.root.removeEventListener("pointercancel", this.pointerCancel, !0), this.root.removeEventListener("keydown", this.keyDown, !0), (t = this.group()) == null || t.remove(), this.activeId = null, this.preview = null);
238
+ }
239
+ assertAlive() {
240
+ if (this.isDestroyed || this.session.disposed || this.view.destroyed)
241
+ throw new Error("顶点编辑扩展已经释放");
242
+ }
243
+ requireGeometry() {
244
+ this.assertAlive();
245
+ const t = this.geometry;
246
+ if (!t || !this.activeId) throw new Error("当前没有正在编辑的自由形状");
247
+ return t;
248
+ }
249
+ group() {
250
+ return this.layer.querySelector("[data-ppt-vertex-editor]");
251
+ }
252
+ slidePoint(t, e) {
253
+ const r = this.activeId, i = this.session.editor.effectiveElement(r);
254
+ return b(this.session.editor.doc, r, {
255
+ x: t.x.value / e.width * i.w,
256
+ y: t.y.value / e.height * i.h
257
+ });
258
+ }
259
+ render() {
260
+ var i;
261
+ (i = this.group()) == null || i.remove();
262
+ const t = this.activeId;
263
+ if (!t || this.isDestroyed || this.view.destroyed || this.view.mode !== "edit" || !this.session.editor.doc.elements[t] || v(this.session.editor.doc, t) !== this.view.slideId) return;
264
+ const e = this.preview ?? f(this.session.editor.doc, t);
265
+ if (!e) return;
266
+ const r = g(this.layer.ownerDocument, "g");
267
+ r.dataset.pptVertexEditor = "", r.dataset.pptVertexElement = t;
268
+ for (const o of e.paths) {
269
+ let h = null, y = null;
270
+ const l = (s, c) => {
271
+ const n = g(this.layer.ownerDocument, "line");
272
+ n.setAttribute("x1", String(s.x)), n.setAttribute("y1", String(s.y)), n.setAttribute("x2", String(c.x)), n.setAttribute("y2", String(c.y)), n.setAttribute("stroke", "#f59e0b"), n.setAttribute("stroke-width", String(1 / this.view.zoom)), n.style.pointerEvents = "none", r.append(n);
273
+ };
274
+ for (const s of o.commands) {
275
+ if (s.type === "arc") throw new Error("顶点查询未物化圆弧命令");
276
+ if (s.type === "close") {
277
+ h = y;
278
+ continue;
279
+ }
280
+ const c = s.points[s.points.length - 1], n = this.slidePoint(c, o), p = s.points.slice(0, -1).map((d) => this.slidePoint(d, o));
281
+ h && p.length && l(h, p[0]), p.length && l(p[p.length - 1], n), s.type === "move" && (y = n);
282
+ for (const d of s.points) {
283
+ const w = this.slidePoint(d, o), a = g(this.layer.ownerDocument, "circle");
284
+ a.dataset.pptVertexPoint = d.id, a.dataset.pptVertexPath = o.id, a.dataset.pptVertexRole = d.role, a.setAttribute("cx", String(w.x)), a.setAttribute("cy", String(w.y)), a.setAttribute("r", String((d.role === "anchor" ? 5 : 4) / this.view.zoom)), a.setAttribute("fill", d.role === "anchor" ? "#fff" : "#fef3c7"), a.setAttribute("stroke", d.role === "anchor" ? "#2563eb" : "#d97706"), a.setAttribute("stroke-width", String(1.5 / this.view.zoom)), a.style.pointerEvents = "all", a.style.cursor = "crosshair", r.append(a);
285
+ }
286
+ h = n;
287
+ }
288
+ }
289
+ this.layer.append(r);
290
+ }
291
+ geometryAt(t, e, r) {
292
+ const i = this.activeId, o = r.paths.find((c) => c.commands.some((n) => n.points.some((p) => p.id === e)));
293
+ if (!o) throw new Error(`找不到顶点路径:${e}`);
294
+ const h = this.stage.getBoundingClientRect(), y = A(t.screen, { left: h.left, top: h.top, zoom: this.view.zoom }), l = x(this.session.editor.doc, i, y), s = this.session.editor.effectiveElement(i);
295
+ return D(r, e, {
296
+ x: l.x / s.w * o.width,
297
+ y: l.y / s.h * o.height
298
+ });
299
+ }
300
+ gesture(t, e) {
301
+ let r = e;
302
+ const i = (o) => {
303
+ r = this.geometryAt(o, t, e), this.preview = r, this.render();
304
+ };
305
+ return {
306
+ cursor: "crosshair",
307
+ dataset: { name: "pptVertexDragging", value: t },
308
+ start: () => {
309
+ },
310
+ frame: i,
311
+ finish: (o) => {
312
+ i(o);
313
+ const h = this.activeId;
314
+ return () => {
315
+ this.preview = null, this.session.editor.exec({ type: "SetGeometry", id: h, geometry: r });
316
+ };
317
+ },
318
+ clear: () => {
319
+ this.preview = null, this.render();
320
+ }
321
+ };
322
+ }
323
+ stop(t) {
324
+ t.preventDefault(), t.stopImmediatePropagation();
325
+ }
326
+ report(t) {
327
+ var e, r;
328
+ this.lifecycle.cancel(), (r = (e = this.options).onError) == null || r.call(e, t), this.options.onError || queueMicrotask(() => {
329
+ throw t;
330
+ });
331
+ }
332
+ }
333
+ function G(u, t, e = {}) {
334
+ return new C(u, t, e);
335
+ }
336
+ export {
337
+ G as createVertexEditor
338
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@web-ppt/editor",
3
- "version": "0.5.0-beta.1",
3
+ "version": "0.5.0-beta.7",
4
4
  "description": "Framework-agnostic browser editing session and DOM view for Web-PPT.",
5
5
  "keywords": [
6
6
  "pptx",
@@ -30,7 +30,43 @@
30
30
  "types": "./dist/index.d.ts",
31
31
  "import": "./dist/editor.js"
32
32
  },
33
- "./package.json": "./package.json"
33
+ "./vertex": {
34
+ "types": "./dist/vertex/index.d.ts",
35
+ "import": "./dist/vertex.js"
36
+ },
37
+ "./adjustments": {
38
+ "types": "./dist/adjustments/index.d.ts",
39
+ "import": "./dist/adjustments.js"
40
+ },
41
+ "./design": {
42
+ "types": "./dist/design/index.d.ts",
43
+ "import": "./dist/design.js"
44
+ },
45
+ "./templates": {
46
+ "types": "./dist/templates/index.d.ts",
47
+ "import": "./dist/templates.js"
48
+ },
49
+ "./chart": {
50
+ "types": "./dist/chart/index.d.ts",
51
+ "import": "./dist/chart.js"
52
+ },
53
+ "./chart-shared": {
54
+ "types": "./dist/chart-shared/index.d.ts",
55
+ "import": "./dist/chart-shared.js"
56
+ },
57
+ "./media": {
58
+ "types": "./dist/media/index.d.ts",
59
+ "import": "./dist/media.js"
60
+ },
61
+ "./package.json": "./package.json",
62
+ "./accessibility": {
63
+ "types": "./dist/canvas-accessibility.d.ts",
64
+ "import": "./dist/accessibility.js"
65
+ },
66
+ "./edit-context": {
67
+ "types": "./dist/edit-context/index.d.ts",
68
+ "import": "./dist/edit-context.js"
69
+ }
34
70
  },
35
71
  "files": [
36
72
  "dist",
@@ -39,14 +75,14 @@
39
75
  "LICENSE"
40
76
  ],
41
77
  "scripts": {
42
- "build": "vite build && tsc -p tsconfig.build.json",
78
+ "build": "vite build && node ../../tooling/check-editor-main-size.mjs && vite build --config vite.vertex.config.ts && vite build --config vite.adjustments.config.ts && vite build --config vite.design.config.ts && vite build --config vite.templates.config.ts && vite build --config vite.chart.config.ts && vite build --config vite.media.config.ts && vite build --config vite.browser-features.config.ts && tsc -p tsconfig.build.json",
43
79
  "prepublishOnly": "npm run build",
44
80
  "prepack": "node ../../tooling/sync-package-docs.mjs"
45
81
  },
46
82
  "peerDependencies": {
47
- "@web-ppt/core": "^0.5.0-beta.1",
48
- "@web-ppt/edit-core": "^0.5.0-beta.1",
49
- "@web-ppt/viewer-core": "^0.5.0-beta.1"
83
+ "@web-ppt/core": "^0.5.0-beta.7",
84
+ "@web-ppt/edit-core": "^0.5.0-beta.7",
85
+ "@web-ppt/viewer-core": "^0.5.0-beta.7"
50
86
  },
51
87
  "publishConfig": {
52
88
  "access": "public"