lucid-extension-sdk 0.0.207 → 0.0.210
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.
- package/README.md +3 -6
- package/commandtypes.d.ts +21 -5
- package/commandtypes.js +1 -0
- package/document/pageproxy.d.ts +6 -2
- package/document/pageproxy.js +7 -2
- package/editorclient.d.ts +5 -0
- package/editorclient.js +14 -0
- package/package.json +1 -1
- package/ui/menu.d.ts +91 -1
- package/ui/menu.js +73 -1
- package/ui/viewport.d.ts +6 -0
- package/ui/viewport.js +8 -0
package/README.md
CHANGED
|
@@ -23,10 +23,9 @@ client.registerAction('my-new-action', () => {
|
|
|
23
23
|
console.log('Hello world');
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
-
menu.
|
|
26
|
+
menu.addDropdownMenuItem({
|
|
27
27
|
label: 'Hello world',
|
|
28
28
|
action: 'my-new-action',
|
|
29
|
-
menuType: MenuType.Main,
|
|
30
29
|
});
|
|
31
30
|
```
|
|
32
31
|
|
|
@@ -67,10 +66,9 @@ client.registerAction('create-content', async () => {
|
|
|
67
66
|
}
|
|
68
67
|
});
|
|
69
68
|
|
|
70
|
-
menu.
|
|
69
|
+
menu.addDropdownMenuItem({
|
|
71
70
|
label: 'Hello world',
|
|
72
71
|
action: 'create-content',
|
|
73
|
-
menuType: MenuType.Main,
|
|
74
72
|
});
|
|
75
73
|
```
|
|
76
74
|
|
|
@@ -103,10 +101,9 @@ client.registerAction('download', async () => {
|
|
|
103
101
|
client.download('data.csv', csv.map((line) => line.join(',')).join('\n'), 'text/plain', false);
|
|
104
102
|
});
|
|
105
103
|
|
|
106
|
-
menu.
|
|
104
|
+
menu.addDropdownMenuItem({
|
|
107
105
|
label: 'Download CSV',
|
|
108
106
|
action: 'download',
|
|
109
|
-
menuType: MenuType.Main,
|
|
110
107
|
location: MenuLocation.Export, //Near File -> Export
|
|
111
108
|
});
|
|
112
109
|
```
|
package/commandtypes.d.ts
CHANGED
|
@@ -122,6 +122,7 @@ export declare const enum CommandName {
|
|
|
122
122
|
SetPackageSettings = "sps",
|
|
123
123
|
SetProperty = "sp",
|
|
124
124
|
SetReferenceKey = "srk",
|
|
125
|
+
SetSelection = "ss",
|
|
125
126
|
SetShapeData = "ssd",
|
|
126
127
|
SetText = "st",
|
|
127
128
|
SetTextStyle = "sts",
|
|
@@ -548,6 +549,10 @@ export type CommandArgs = {
|
|
|
548
549
|
query: SetReferenceKeyQuery;
|
|
549
550
|
result: SetReferenceKeyResult;
|
|
550
551
|
};
|
|
552
|
+
[CommandName.SetSelection]: {
|
|
553
|
+
query: SetSelectionQuery;
|
|
554
|
+
result: SetSelectionResult;
|
|
555
|
+
};
|
|
551
556
|
[CommandName.SetShapeData]: {
|
|
552
557
|
query: SetShapeDataQuery;
|
|
553
558
|
result: SetShapeDataResult;
|
|
@@ -1034,11 +1039,17 @@ export type GetLLMContextFromItemsQuery = {
|
|
|
1034
1039
|
/** List of item IDs to return LLM-readable context for */
|
|
1035
1040
|
'i': string[];
|
|
1036
1041
|
};
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
+
export type GetLLMContextFromItemsResult = {
|
|
1043
|
+
/**
|
|
1044
|
+
* A string in a format that LLMs like ChatGPT can easily understand, representing the content
|
|
1045
|
+
* of the items specified as well as their immediate context if necessary
|
|
1046
|
+
*/
|
|
1047
|
+
'p': string;
|
|
1048
|
+
/**
|
|
1049
|
+
* A map from IDs specified in the prompt string to actual Lucid item IDs
|
|
1050
|
+
*/
|
|
1051
|
+
'id': Record<string, string>;
|
|
1052
|
+
};
|
|
1042
1053
|
export type GetReferenceKeyQuery = {
|
|
1043
1054
|
/** ID of the LucidElement to read a reference key from, or undefined to read from the LucidDocument */
|
|
1044
1055
|
'id'?: string | undefined;
|
|
@@ -1370,6 +1381,11 @@ export type SetReferenceKeyQuery = {
|
|
|
1370
1381
|
'v'?: SerializedReferenceKeyType | undefined;
|
|
1371
1382
|
};
|
|
1372
1383
|
export type SetReferenceKeyResult = undefined;
|
|
1384
|
+
export type SetSelectionQuery = {
|
|
1385
|
+
/** IDs of the elements to select */
|
|
1386
|
+
'i': string[];
|
|
1387
|
+
};
|
|
1388
|
+
export type SetSelectionResult = undefined;
|
|
1373
1389
|
export type SetShapeDataQuery = {
|
|
1374
1390
|
/** ID of the element to set this shape data on */
|
|
1375
1391
|
'id'?: string | undefined;
|
package/commandtypes.js
CHANGED
|
@@ -99,6 +99,7 @@ exports.commandTitles = new Map([
|
|
|
99
99
|
["sps" /* CommandName.SetPackageSettings */, 'SetPackageSettings'],
|
|
100
100
|
["sp" /* CommandName.SetProperty */, 'SetProperty'],
|
|
101
101
|
["srk" /* CommandName.SetReferenceKey */, 'SetReferenceKey'],
|
|
102
|
+
["ss" /* CommandName.SetSelection */, 'SetSelection'],
|
|
102
103
|
["ssd" /* CommandName.SetShapeData */, 'SetShapeData'],
|
|
103
104
|
["sts" /* CommandName.SetTextStyle */, 'SetTextStyle'],
|
|
104
105
|
["sm" /* CommandName.ShowModal */, 'ShowModal'],
|
package/document/pageproxy.d.ts
CHANGED
|
@@ -111,7 +111,11 @@ export declare class PageProxy extends ElementProxy {
|
|
|
111
111
|
/**
|
|
112
112
|
* @param items
|
|
113
113
|
* @returns A string representing the content of the items provided, including immediate surrounding context if
|
|
114
|
-
* necessary, in a format that is easily understandable by LLMs like ChatGPT.
|
|
114
|
+
* necessary, in a format that is easily understandable by LLMs like ChatGPT. Also a map of IDs, from the shortened
|
|
115
|
+
* IDs provided for the items in the context to the actual Lucid item IDs.
|
|
115
116
|
*/
|
|
116
|
-
getLLMContextForItems(items: ItemProxy[]):
|
|
117
|
+
getLLMContextForItems(items: ItemProxy[]): {
|
|
118
|
+
prompt: string;
|
|
119
|
+
idToLucidId: Map<string, string>;
|
|
120
|
+
};
|
|
117
121
|
}
|
package/document/pageproxy.js
CHANGED
|
@@ -177,15 +177,20 @@ class PageProxy extends elementproxy_1.ElementProxy {
|
|
|
177
177
|
/**
|
|
178
178
|
* @param items
|
|
179
179
|
* @returns A string representing the content of the items provided, including immediate surrounding context if
|
|
180
|
-
* necessary, in a format that is easily understandable by LLMs like ChatGPT.
|
|
180
|
+
* necessary, in a format that is easily understandable by LLMs like ChatGPT. Also a map of IDs, from the shortened
|
|
181
|
+
* IDs provided for the items in the context to the actual Lucid item IDs.
|
|
181
182
|
*/
|
|
182
183
|
getLLMContextForItems(items) {
|
|
183
184
|
//We don't check that the items are on this page here; that is done in the implementation
|
|
184
185
|
//of the API command. It would be a lot of extra API calls for no benefit.
|
|
185
|
-
|
|
186
|
+
const result = this.client.sendCommand("llm" /* CommandName.GetLLMContextFromItems */, {
|
|
186
187
|
'p': this.id,
|
|
187
188
|
'i': items.map((item) => item.id),
|
|
188
189
|
});
|
|
190
|
+
return {
|
|
191
|
+
prompt: result['p'],
|
|
192
|
+
idToLucidId: new Map(Object.entries(result['id'])),
|
|
193
|
+
};
|
|
189
194
|
}
|
|
190
195
|
}
|
|
191
196
|
exports.PageProxy = PageProxy;
|
package/editorclient.d.ts
CHANGED
|
@@ -317,6 +317,11 @@ export declare class EditorClient {
|
|
|
317
317
|
* @returns the given item
|
|
318
318
|
*/
|
|
319
319
|
getItemProxy(id: string): BlockProxy | LineProxy | GroupProxy;
|
|
320
|
+
/**
|
|
321
|
+
* @param id ID of the item to create a proxy for
|
|
322
|
+
* @returns the given item, or undefined if the item does not exist or an error occurs
|
|
323
|
+
*/
|
|
324
|
+
tryGetItemProxy(id: string): BlockProxy | LineProxy | GroupProxy | undefined;
|
|
320
325
|
/**
|
|
321
326
|
* @param id ID of the element to create a proxy for
|
|
322
327
|
* @returns the given element
|
package/editorclient.js
CHANGED
|
@@ -516,6 +516,20 @@ class EditorClient {
|
|
|
516
516
|
throw new Error('Element ' + id + ' is not an Item; type found is ' + type);
|
|
517
517
|
}
|
|
518
518
|
}
|
|
519
|
+
/**
|
|
520
|
+
* @param id ID of the item to create a proxy for
|
|
521
|
+
* @returns the given item, or undefined if the item does not exist or an error occurs
|
|
522
|
+
*/
|
|
523
|
+
tryGetItemProxy(id) {
|
|
524
|
+
try {
|
|
525
|
+
const item = this.getItemProxy(id);
|
|
526
|
+
if (item.exists()) {
|
|
527
|
+
return item;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
catch (_a) { }
|
|
531
|
+
return undefined;
|
|
532
|
+
}
|
|
519
533
|
/**
|
|
520
534
|
* @param id ID of the element to create a proxy for
|
|
521
535
|
* @returns the given element
|
package/package.json
CHANGED
package/ui/menu.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { EditorClient } from '../editorclient';
|
|
2
|
+
/**
|
|
3
|
+
* Type of menu you're adding.
|
|
4
|
+
* Consider using addDropdownMenuItem, addContextMenuItem, or addContentDockMenuItem for easier to use entrypoints and clearer requirements.
|
|
5
|
+
*/
|
|
2
6
|
export declare enum MenuType {
|
|
7
|
+
/** The main drop down menus. */
|
|
3
8
|
Main = 1,
|
|
4
9
|
/** The context menu that appears when the user right-clicks the canvas. */
|
|
5
10
|
Context = 2,
|
|
11
|
+
/** The side dock in Lucidspark and Teamspaces */
|
|
6
12
|
ContentDock = 3
|
|
7
13
|
}
|
|
8
14
|
/**
|
|
@@ -17,6 +23,56 @@ export declare enum MenuLocation {
|
|
|
17
23
|
Export = 5,
|
|
18
24
|
Import = 6
|
|
19
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Data to create a menu
|
|
28
|
+
*/
|
|
29
|
+
export interface CustomMenuConfig {
|
|
30
|
+
/** The text to display on the menu item */
|
|
31
|
+
label: string;
|
|
32
|
+
/** The registered action to run when the menu item is clicked. */
|
|
33
|
+
action?: string;
|
|
34
|
+
/** If specified, what action's return value should determine if this menu item is visible? */
|
|
35
|
+
visibleAction?: string;
|
|
36
|
+
/** If specified, what action's return value should determine if this menu item is disabled? */
|
|
37
|
+
disabledAction?: string;
|
|
38
|
+
/** If specified, this menu item should launch a file picker */
|
|
39
|
+
file?: {
|
|
40
|
+
/** An action registered with EditorClient.registerFileUploadAction */
|
|
41
|
+
action: string;
|
|
42
|
+
/** An [accept string](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) as specified for HTML file inputs */
|
|
43
|
+
accept: string;
|
|
44
|
+
/** If true, only allow a single file to be selected for upload */
|
|
45
|
+
singleFileOnly?: boolean;
|
|
46
|
+
/** If true, send the file contents to the callback action as a Uint8Array as well as a plain text string */
|
|
47
|
+
binary?: boolean;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Data to create a dropdown menu in the main top menus.
|
|
52
|
+
*/
|
|
53
|
+
export interface CustomDropdownMenu extends CustomMenuConfig {
|
|
54
|
+
/** Where in that menu to display this item in Lucidchart.
|
|
55
|
+
* Defaults to the extension menu.
|
|
56
|
+
* In Lucidspark the only valid menu is Extension, it will not appear otherwise.
|
|
57
|
+
*/
|
|
58
|
+
location?: MenuLocation;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Data to create a content dock icon in Spark + Teamspaces
|
|
62
|
+
*/
|
|
63
|
+
export interface CustomContentDockMenu extends CustomMenuConfig {
|
|
64
|
+
/** The icon to display on the menu item.
|
|
65
|
+
* A URL (a data URI is fine) pointing to an icon representing the integration.
|
|
66
|
+
* This will be displayed at up to 32x32 CSS pixels in size.
|
|
67
|
+
*/
|
|
68
|
+
iconUrl: string;
|
|
69
|
+
/** The registered action to run when the menu item is clicked */
|
|
70
|
+
action: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* A generic object to contain any information creating a menu might need.
|
|
74
|
+
* It is a little easier to use CustomMenuConfig, CustomDropdownMenu, or CustomContentDockMenu.
|
|
75
|
+
*/
|
|
20
76
|
export interface CustomMenuItem {
|
|
21
77
|
/** The text to display on the menu item */
|
|
22
78
|
label: string;
|
|
@@ -58,11 +114,45 @@ export declare class Menu {
|
|
|
58
114
|
private readonly client;
|
|
59
115
|
constructor(client: EditorClient);
|
|
60
116
|
/**
|
|
61
|
-
*
|
|
117
|
+
* Generic function to create a new menu item to trigger custom code.
|
|
118
|
+
* You can use addDropdownMenuItem, addContextMenuItem, or addContentDockMenuItem for easier to use entrypoints.
|
|
119
|
+
*
|
|
120
|
+
* The action must be registered with
|
|
62
121
|
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
63
122
|
* prior to using it in the menu.
|
|
64
123
|
*
|
|
65
124
|
* @param item The definition of the new menu item
|
|
66
125
|
*/
|
|
67
126
|
addMenuItem(item: CustomMenuItem): void;
|
|
127
|
+
/**
|
|
128
|
+
* Create a menu in the basic drop down top menus. In Lucidspark this will just be under the generic menu.
|
|
129
|
+
* In Lucidchart you can configure this to be in any of the other drop down menus.
|
|
130
|
+
*
|
|
131
|
+
* The action must be registered with
|
|
132
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
133
|
+
* prior to using it in the menu.
|
|
134
|
+
*
|
|
135
|
+
* @param item The definition of the new menu item
|
|
136
|
+
*/
|
|
137
|
+
addDropdownMenuItem(item: CustomDropdownMenu): void;
|
|
138
|
+
/**
|
|
139
|
+
* Create a menu in the right click context menu. Appears in both Lucidspark and Lucidchart.
|
|
140
|
+
*
|
|
141
|
+
* The action must be registered with
|
|
142
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
143
|
+
* prior to using it in the menu.
|
|
144
|
+
*
|
|
145
|
+
* @param item The definition of the new menu item
|
|
146
|
+
*/
|
|
147
|
+
addContextMenuItem(item: CustomMenuConfig): void;
|
|
148
|
+
/**
|
|
149
|
+
* Create an icon tied to an action (required) that appears in the left toolbar in Lucidspark and Teamspaces (not in Lucidchart).
|
|
150
|
+
*
|
|
151
|
+
* The action must be registered with
|
|
152
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
153
|
+
* prior to using it in the menu.
|
|
154
|
+
*
|
|
155
|
+
* @param item The definition of the new menu item
|
|
156
|
+
*/
|
|
157
|
+
addContentDockMenuItem(item: CustomContentDockMenu): void;
|
|
68
158
|
}
|
package/ui/menu.js
CHANGED
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Menu = exports.MenuLocation = exports.MenuType = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Type of menu you're adding.
|
|
6
|
+
* Consider using addDropdownMenuItem, addContextMenuItem, or addContentDockMenuItem for easier to use entrypoints and clearer requirements.
|
|
7
|
+
*/
|
|
4
8
|
var MenuType;
|
|
5
9
|
(function (MenuType) {
|
|
10
|
+
/** The main drop down menus. */
|
|
6
11
|
MenuType[MenuType["Main"] = 1] = "Main";
|
|
7
12
|
/** The context menu that appears when the user right-clicks the canvas. */
|
|
8
13
|
MenuType[MenuType["Context"] = 2] = "Context";
|
|
14
|
+
/** The side dock in Lucidspark and Teamspaces */
|
|
9
15
|
MenuType[MenuType["ContentDock"] = 3] = "ContentDock";
|
|
10
16
|
})(MenuType = exports.MenuType || (exports.MenuType = {}));
|
|
11
17
|
/**
|
|
@@ -26,7 +32,10 @@ class Menu {
|
|
|
26
32
|
this.client = client;
|
|
27
33
|
}
|
|
28
34
|
/**
|
|
29
|
-
*
|
|
35
|
+
* Generic function to create a new menu item to trigger custom code.
|
|
36
|
+
* You can use addDropdownMenuItem, addContextMenuItem, or addContentDockMenuItem for easier to use entrypoints.
|
|
37
|
+
*
|
|
38
|
+
* The action must be registered with
|
|
30
39
|
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
31
40
|
* prior to using it in the menu.
|
|
32
41
|
*
|
|
@@ -66,5 +75,68 @@ class Menu {
|
|
|
66
75
|
: undefined,
|
|
67
76
|
});
|
|
68
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Create a menu in the basic drop down top menus. In Lucidspark this will just be under the generic menu.
|
|
80
|
+
* In Lucidchart you can configure this to be in any of the other drop down menus.
|
|
81
|
+
*
|
|
82
|
+
* The action must be registered with
|
|
83
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
84
|
+
* prior to using it in the menu.
|
|
85
|
+
*
|
|
86
|
+
* @param item The definition of the new menu item
|
|
87
|
+
*/
|
|
88
|
+
addDropdownMenuItem(item) {
|
|
89
|
+
var _a;
|
|
90
|
+
this.addMenuItem({
|
|
91
|
+
label: item.label,
|
|
92
|
+
action: item.action,
|
|
93
|
+
visibleAction: item.visibleAction,
|
|
94
|
+
disabledAction: item.disabledAction,
|
|
95
|
+
menuType: MenuType.Main,
|
|
96
|
+
location: (_a = item.location) !== null && _a !== void 0 ? _a : MenuLocation.Extension,
|
|
97
|
+
file: item.file,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Create a menu in the right click context menu. Appears in both Lucidspark and Lucidchart.
|
|
102
|
+
*
|
|
103
|
+
* The action must be registered with
|
|
104
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
105
|
+
* prior to using it in the menu.
|
|
106
|
+
*
|
|
107
|
+
* @param item The definition of the new menu item
|
|
108
|
+
*/
|
|
109
|
+
addContextMenuItem(item) {
|
|
110
|
+
this.addMenuItem({
|
|
111
|
+
label: item.label,
|
|
112
|
+
action: item.action,
|
|
113
|
+
visibleAction: item.visibleAction,
|
|
114
|
+
disabledAction: item.disabledAction,
|
|
115
|
+
menuType: MenuType.Context,
|
|
116
|
+
location: MenuLocation.Extension,
|
|
117
|
+
file: item.file,
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Create an icon tied to an action (required) that appears in the left toolbar in Lucidspark and Teamspaces (not in Lucidchart).
|
|
122
|
+
*
|
|
123
|
+
* The action must be registered with
|
|
124
|
+
* [EditorClient.registerAction](/extension-sdk/#classes_editorclient-EditorClient_registeraction)
|
|
125
|
+
* prior to using it in the menu.
|
|
126
|
+
*
|
|
127
|
+
* @param item The definition of the new menu item
|
|
128
|
+
*/
|
|
129
|
+
addContentDockMenuItem(item) {
|
|
130
|
+
this.addMenuItem({
|
|
131
|
+
label: item.label,
|
|
132
|
+
iconUrl: item.iconUrl,
|
|
133
|
+
action: item.action,
|
|
134
|
+
visibleAction: item.visibleAction,
|
|
135
|
+
disabledAction: item.disabledAction,
|
|
136
|
+
menuType: MenuType.ContentDock,
|
|
137
|
+
location: MenuLocation.Extension,
|
|
138
|
+
file: item.file,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
69
141
|
}
|
|
70
142
|
exports.Menu = Menu;
|
package/ui/viewport.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export declare class Viewport {
|
|
|
21
21
|
* @returns An array of currently-selected items on the currently-visible page
|
|
22
22
|
*/
|
|
23
23
|
getSelectedItems(deep?: boolean): ItemProxy[];
|
|
24
|
+
/**
|
|
25
|
+
* Unselect all currently-selected items, and select the subset of the given items that exist on
|
|
26
|
+
* the currently-visible page.
|
|
27
|
+
* @param items
|
|
28
|
+
*/
|
|
29
|
+
setSelectedItems(items: ItemProxy[]): void;
|
|
24
30
|
/**
|
|
25
31
|
* Find available space on the current page for adding new content.
|
|
26
32
|
* @param width
|
package/ui/viewport.js
CHANGED
|
@@ -23,6 +23,14 @@ class Viewport {
|
|
|
23
23
|
.map((id) => this.client.getElementProxy(id))
|
|
24
24
|
.filter((proxy) => proxy instanceof itemproxy_1.ItemProxy);
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Unselect all currently-selected items, and select the subset of the given items that exist on
|
|
28
|
+
* the currently-visible page.
|
|
29
|
+
* @param items
|
|
30
|
+
*/
|
|
31
|
+
setSelectedItems(items) {
|
|
32
|
+
this.client.sendCommand("ss" /* CommandName.SetSelection */, { 'i': items.map((i) => i.id) });
|
|
33
|
+
}
|
|
26
34
|
/**
|
|
27
35
|
* Find available space on the current page for adding new content.
|
|
28
36
|
* @param width
|