@ctzhian/tiptap 1.12.8 → 1.12.10

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.
@@ -152,7 +152,7 @@ var Reader = function Reader() {
152
152
  }
153
153
  return onUpload;
154
154
  }(),
155
- content: ''
155
+ content: "<ol class=\"ordered-list\" data-type=\"orderedList\"><li><p>jfdlsaf</p></li><li><p>sadfsa</p><ol class=\"ordered-list\" data-type=\"orderedList\"><li><p>fjasldkfj</p><ol class=\"ordered-list\" data-type=\"orderedList\"><li><p>fasd</p><ol class=\"ordered-list\" data-type=\"orderedList\"><li><p>fasdjklf</p></li></ol></li><li><p>fjdslkf</p></li><li><p>sjfsadlf</p></li></ol></li></ol></li></ol><ul class=\"bullet-list\" data-type=\"bulletList\"><li><p>fsldjakfjlksd</p></li><li><p>fsadfasd</p><ul class=\"bullet-list\" data-type=\"bulletList\"><li><p>fajsdlkfj</p></li><li><p>fasdjklf</p><ul class=\"bullet-list\" data-type=\"bulletList\"><li><p>fjasldfsa</p><ul class=\"bullet-list\" data-type=\"bulletList\"><li><p>fsadlkj</p></li></ul></li></ul></li></ul></li></ul><p></p>"
156
156
  }),
157
157
  editor = _useTiptap.editor;
158
158
  return /*#__PURE__*/React.createElement(EditorThemeProvider, {
@@ -10,11 +10,10 @@ function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try
10
10
  function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }
11
11
  import { Box, Divider, Stack, Typography } from "@mui/material";
12
12
  import React from "react";
13
- import { AddCircleFillIcon, ArrowDownSLineIcon, AttachmentLineIcon, BoldIcon, CheckboxCircleFillIcon, CloseCircleFillIcon, CodeBoxLineIcon, CodeLineIcon, DoubleQuotesLIcon, ErrorWarningFillIcon, Folder2LineIcon, FunctionsIcon, H1Icon, H2Icon, H3Icon, H4Icon, H5Icon, H6Icon, ImageLineIcon, Information2FillIcon, Information2LineIcon, ItalicIcon, LinkIcon, ListCheck3Icon, ListOrdered2Icon, ListUnorderedIcon, MarkPenLineIcon, MenuFold2FillIcon, MovieLineIcon, Music2LineIcon, SeparatorIcon, SquareRootIcon, StrikethroughIcon, SubscriptIcon, SuperscriptIcon, Table2Icon, UnderlineIcon, UserSmileFillIcon } from "../component/Icons";
13
+ import { AddCircleFillIcon, ArrowDownSLineIcon, AttachmentLineIcon, BoldIcon, CheckboxCircleFillIcon, CloseCircleFillIcon, CodeBoxLineIcon, CodeLineIcon, CodeSSlashLineIcon, DoubleQuotesLIcon, ErrorWarningFillIcon, Folder2LineIcon, FormulaIcon, FunctionsIcon, H1Icon, H2Icon, H3Icon, H4Icon, H5Icon, H6Icon, ImageLineIcon, Information2FillIcon, Information2LineIcon, ItalicIcon, LinkIcon, ListCheck3Icon, ListOrdered2Icon, ListUnorderedIcon, MarkPenLineIcon, MenuFold2FillIcon, MovieLineIcon, Music2LineIcon, SeparatorIcon, SquareRootIcon, StrikethroughIcon, SubscriptIcon, SuperscriptIcon, Table2Icon, UnderlineIcon, UserSmileFillIcon } from "../component/Icons";
14
14
  import Menu from "../component/Menu";
15
15
  import { ToolbarItem } from "../component/Toolbar";
16
16
  import TableSizePicker from "../component/Toolbar/TableSizePicker";
17
- import { getFileType } from "../util/fileHandler";
18
17
  var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
19
18
  var aceEditorRef = _ref.aceEditorRef,
20
19
  isExpend = _ref.isExpend,
@@ -25,7 +24,7 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
25
24
  var attachmentInputRef = React.useRef(null);
26
25
  var handleFileUpload = /*#__PURE__*/function () {
27
26
  var _ref2 = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(file, expectedType) {
28
- var url, fileType, content;
27
+ var url, content;
29
28
  return _regeneratorRuntime().wrap(function _callee$(_context) {
30
29
  while (1) switch (_context.prev = _context.next) {
31
30
  case 0:
@@ -37,43 +36,34 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
37
36
  case 2:
38
37
  _context.prev = 2;
39
38
  _context.next = 5;
40
- return onUpload(file, function (_ref3) {
41
- var progress = _ref3.progress;
42
- // 可以在这里显示上传进度
43
- console.log('Upload progress:', progress);
44
- });
39
+ return onUpload(file);
45
40
  case 5:
46
41
  url = _context.sent;
47
- fileType = getFileType(file);
48
- content = ''; // 根据文件类型插入对应的内容
42
+ content = '';
49
43
  if (expectedType === 'image') {
50
- // 图片:插入 [file.name](url)
51
44
  content = "![".concat(file.name, "](").concat(url, ")");
52
45
  } else if (expectedType === 'video') {
53
- // 视频:插入 <video src="url"></video>
54
46
  content = "<p>\n<video src=\"".concat(url, "\" controls=\"true\"></video>\n</p>");
55
47
  } else if (expectedType === 'audio') {
56
- // 音频:插入 <audio src="url"></audio>
57
48
  content = "<p>\n<audio src=\"".concat(url, "\" controls=\"true\"></audio>\n</p>");
58
49
  } else {
59
- // 附件:插入 <a href="url" download="file.name">file.name</a>
60
50
  content = "<p>\n<a href=\"".concat(url, "\" download=\"").concat(file.name, "\">").concat(file.name, "</a>\n</p>");
61
51
  }
62
52
  insertTextAndFocusPositionRow({
63
53
  text: content,
64
54
  block: true
65
55
  });
66
- _context.next = 15;
56
+ _context.next = 14;
67
57
  break;
68
- case 12:
69
- _context.prev = 12;
58
+ case 11:
59
+ _context.prev = 11;
70
60
  _context.t0 = _context["catch"](2);
71
61
  console.error('文件上传失败:', _context.t0);
72
- case 15:
62
+ case 14:
73
63
  case "end":
74
64
  return _context.stop();
75
65
  }
76
- }, _callee, null, [[2, 12]]);
66
+ }, _callee, null, [[2, 11]]);
77
67
  }));
78
68
  return function handleFileUpload(_x, _x2) {
79
69
  return _ref2.apply(this, arguments);
@@ -350,8 +340,68 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
350
340
  block: true
351
341
  });
352
342
  }
353
- }, {
343
+ }].concat(_toConsumableArray(isExpend ? [{
354
344
  id: 'divider-2'
345
+ }, {
346
+ id: 'separator',
347
+ icon: /*#__PURE__*/React.createElement(SeparatorIcon, {
348
+ sx: {
349
+ fontSize: '1rem'
350
+ }
351
+ }),
352
+ label: '分割线',
353
+ onClick: function onClick() {
354
+ insertTextAndFocusPositionRow({
355
+ text: '---',
356
+ position: 3,
357
+ block: true
358
+ });
359
+ }
360
+ }, {
361
+ id: 'blockquote',
362
+ icon: /*#__PURE__*/React.createElement(DoubleQuotesLIcon, {
363
+ sx: {
364
+ fontSize: '1rem'
365
+ }
366
+ }),
367
+ label: '引用',
368
+ onClick: function onClick() {
369
+ insertTextAndFocusPositionRow({
370
+ text: '> ',
371
+ position: 2,
372
+ block: true
373
+ });
374
+ }
375
+ }, {
376
+ id: 'details',
377
+ icon: /*#__PURE__*/React.createElement(MenuFold2FillIcon, {
378
+ sx: {
379
+ fontSize: '1rem'
380
+ }
381
+ }),
382
+ label: '折叠面板',
383
+ onClick: function onClick() {
384
+ insertTextAndFocusPositionRow({
385
+ text: ':::details\n\n:::detailsSummary\n\n:::\n\n:::detailsContent\n\n:::\n\n:::\n',
386
+ row: 1,
387
+ block: true
388
+ });
389
+ }
390
+ }, {
391
+ id: 'alert',
392
+ icon: /*#__PURE__*/React.createElement(Information2LineIcon, {
393
+ sx: {
394
+ fontSize: '1rem'
395
+ }
396
+ }),
397
+ label: '警告块',
398
+ onClick: function onClick() {
399
+ insertTextAndFocusPositionRow({
400
+ text: ':::alert {variant="info"}\n\n:::',
401
+ row: -1,
402
+ block: true
403
+ });
404
+ }
355
405
  }, {
356
406
  id: 'inline-math',
357
407
  icon: /*#__PURE__*/React.createElement(SquareRootIcon, {
@@ -410,7 +460,7 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
410
460
  block: true
411
461
  });
412
462
  }
413
- }, {
463
+ }] : []), [{
414
464
  id: 'divider-3'
415
465
  }, {
416
466
  id: 'link',
@@ -440,7 +490,7 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
440
490
  position: 7
441
491
  });
442
492
  }
443
- }];
493
+ }]);
444
494
  return /*#__PURE__*/React.createElement(Stack, {
445
495
  direction: 'row',
446
496
  alignItems: 'center'
@@ -719,6 +769,92 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
719
769
  });
720
770
  }
721
771
  }]
772
+ }, {
773
+ customLabel: /*#__PURE__*/React.createElement(Typography, {
774
+ sx: {
775
+ px: 1,
776
+ pt: 2,
777
+ fontSize: '12px',
778
+ color: 'text.disabled'
779
+ }
780
+ }, "\u7A0B\u5E8F\u5458\u4E13\u7528"),
781
+ key: 'programmer'
782
+ }, {
783
+ label: '代码',
784
+ key: 'code',
785
+ icon: /*#__PURE__*/React.createElement(CodeSSlashLineIcon, {
786
+ sx: {
787
+ fontSize: '1rem'
788
+ }
789
+ }),
790
+ children: [{
791
+ label: '行内代码',
792
+ key: 'inlineCode',
793
+ icon: /*#__PURE__*/React.createElement(CodeLineIcon, {
794
+ sx: {
795
+ fontSize: '1rem'
796
+ }
797
+ }),
798
+ onClick: function onClick() {
799
+ return insertTextAndFocusPositionRow({
800
+ text: '`',
801
+ position: 1
802
+ });
803
+ }
804
+ }, {
805
+ label: '代码块',
806
+ key: 'codeBlock',
807
+ icon: /*#__PURE__*/React.createElement(CodeBoxLineIcon, {
808
+ sx: {
809
+ fontSize: '1rem'
810
+ }
811
+ }),
812
+ onClick: function onClick() {
813
+ return insertTextAndFocusPositionRow({
814
+ text: '```\n\n```',
815
+ row: 1,
816
+ block: true
817
+ });
818
+ }
819
+ }]
820
+ }, {
821
+ label: '数学公式',
822
+ key: 'math',
823
+ icon: /*#__PURE__*/React.createElement(FormulaIcon, {
824
+ sx: {
825
+ fontSize: '1rem'
826
+ }
827
+ }),
828
+ children: [{
829
+ label: '行内数学公式',
830
+ key: 'inline-math',
831
+ icon: /*#__PURE__*/React.createElement(SquareRootIcon, {
832
+ sx: {
833
+ fontSize: '1rem'
834
+ }
835
+ }),
836
+ onClick: function onClick() {
837
+ insertTextAndFocusPositionRow({
838
+ text: '$$',
839
+ position: 1
840
+ });
841
+ }
842
+ }, {
843
+ label: '块级数学公式',
844
+ key: 'block-math',
845
+ icon: /*#__PURE__*/React.createElement(FunctionsIcon, {
846
+ sx: {
847
+ fontSize: '1rem'
848
+ }
849
+ }),
850
+ onClick: function onClick() {
851
+ insertTextAndFocusPositionRow({
852
+ text: '$$\n\n$$',
853
+ row: 1,
854
+ block: true
855
+ });
856
+ }
857
+ }]
722
858
  }]
723
859
  }), /*#__PURE__*/React.createElement(Divider, {
724
860
  sx: {
@@ -796,6 +932,62 @@ var EditorMarkdownToolbar = function EditorMarkdownToolbar(_ref) {
796
932
  icon: it.icon,
797
933
  onClick: it === null || it === void 0 ? void 0 : it.onClick
798
934
  });
935
+ }), isExpend && /*#__PURE__*/React.createElement(Menu, {
936
+ context: /*#__PURE__*/React.createElement(ToolbarItem, {
937
+ tip: '表格',
938
+ icon: /*#__PURE__*/React.createElement(Table2Icon, {
939
+ sx: {
940
+ fontSize: '1rem'
941
+ }
942
+ })
943
+ }),
944
+ anchorOrigin: {
945
+ vertical: 'bottom',
946
+ horizontal: 'left'
947
+ },
948
+ transformOrigin: {
949
+ vertical: 'top',
950
+ horizontal: 'left'
951
+ },
952
+ arrowIcon: /*#__PURE__*/React.createElement(ArrowDownSLineIcon, {
953
+ sx: {
954
+ fontSize: '1rem',
955
+ transform: 'rotate(-90deg)'
956
+ }
957
+ }),
958
+ zIndex: isExpend ? 2100 : undefined,
959
+ list: [{
960
+ key: 'table-size-picker',
961
+ customLabel: /*#__PURE__*/React.createElement(TableSizePicker, {
962
+ onConfirm: function onConfirm(cols, rows) {
963
+ var headerRow = "| ".concat(Array.from({
964
+ length: cols
965
+ }).map(function () {
966
+ return '';
967
+ }).join(' | '), " |\n");
968
+ var separatorRow = "| ".concat(Array.from({
969
+ length: cols
970
+ }).map(function () {
971
+ return '---';
972
+ }).join(' | '), " |\n");
973
+ var dataRows = Array.from({
974
+ length: rows
975
+ }).map(function () {
976
+ return "| ".concat(Array.from({
977
+ length: cols
978
+ }).map(function () {
979
+ return '';
980
+ }).join(' | '), " |\n");
981
+ }).join('');
982
+ var tableMarkdown = "".concat(headerRow).concat(separatorRow).concat(dataRows);
983
+ insertTextAndFocusPositionRow({
984
+ text: tableMarkdown,
985
+ position: 1,
986
+ block: true
987
+ });
988
+ }
989
+ })
990
+ }]
799
991
  }), /*#__PURE__*/React.createElement("input", {
800
992
  ref: imageInputRef,
801
993
  type: "file",
@@ -8,9 +8,6 @@ export declare const PLACEHOLDER: {
8
8
  6: string;
9
9
  };
10
10
  detailsSummary: string;
11
- orderedList: string;
12
- bulletList: string;
13
- taskList: string;
14
11
  paragraph: string;
15
12
  default: string;
16
13
  };
@@ -8,9 +8,6 @@ export var PLACEHOLDER = {
8
8
  6: '六级标题'
9
9
  },
10
10
  detailsSummary: '输入面板标题',
11
- orderedList: '列表',
12
- bulletList: '列表',
13
- taskList: '列表',
14
11
  paragraph: '输入 "/" 插入内容',
15
12
  default: '写点什么吧,输入 "/" 插入内容'
16
13
  };
@@ -66,7 +66,10 @@ var AlertView = function AlertView(_ref) {
66
66
  setAnchorEl = _useState2[1];
67
67
  var showIcon = attrs.type !== 'text';
68
68
  var variantData = useMemo(function () {
69
- return VARIANT_DATA[attrs.variant || 'info'];
69
+ if (attrs.variant && VARIANT_DATA[attrs.variant]) {
70
+ return VARIANT_DATA[attrs.variant];
71
+ }
72
+ return VARIANT_DATA['default'];
70
73
  }, [attrs.variant]);
71
74
  var handleShowOperationPopover = function handleShowOperationPopover(event) {
72
75
  return setAnchorEl(event.currentTarget);
@@ -1,8 +1,10 @@
1
+ import { ChromeIcon } from "../../../component/Icons";
2
+ import { Avatar, Box } from "@mui/material";
1
3
  import { useTheme } from "@mui/material/styles";
2
4
  import { MarkViewContent } from "@tiptap/react";
3
5
  import React, { useCallback } from "react";
4
6
  var LinkViewWrapper = function LinkViewWrapper(_ref) {
5
- var _mark$attrs2, _mark$attrs3, _mark$attrs4;
7
+ var _mark$attrs2, _mark$attrs3, _mark$attrs4, _mark$attrs6;
6
8
  var editor = _ref.editor,
7
9
  mark = _ref.mark;
8
10
  var theme = useTheme();
@@ -24,6 +26,11 @@ var LinkViewWrapper = function LinkViewWrapper(_ref) {
24
26
  var href = (mark === null || mark === void 0 || (_mark$attrs2 = mark.attrs) === null || _mark$attrs2 === void 0 ? void 0 : _mark$attrs2.href) || '';
25
27
  var target = (mark === null || mark === void 0 || (_mark$attrs3 = mark.attrs) === null || _mark$attrs3 === void 0 ? void 0 : _mark$attrs3.target) || '_blank';
26
28
  var download = mark === null || mark === void 0 || (_mark$attrs4 = mark.attrs) === null || _mark$attrs4 === void 0 ? void 0 : _mark$attrs4.download;
29
+ var favicon = '';
30
+ try {
31
+ var _mark$attrs5;
32
+ favicon = mark !== null && mark !== void 0 && (_mark$attrs5 = mark.attrs) !== null && _mark$attrs5 !== void 0 && _mark$attrs5.href ? new URL(mark.attrs.href).origin + '/favicon.ico' : '';
33
+ } catch (err) {}
27
34
  return /*#__PURE__*/React.createElement(MarkViewContent, {
28
35
  as: "a",
29
36
  href: href,
@@ -33,8 +40,29 @@ var LinkViewWrapper = function LinkViewWrapper(_ref) {
33
40
  style: {
34
41
  color: theme.palette.primary.main,
35
42
  textDecoration: 'underline',
36
- cursor: 'pointer'
43
+ cursor: 'pointer',
44
+ display: 'inline-flex',
45
+ alignItems: 'center',
46
+ gap: '2px'
37
47
  }
38
- });
48
+ }, /*#__PURE__*/React.createElement(Avatar, {
49
+ sx: {
50
+ width: '1rem',
51
+ height: '1rem',
52
+ alignSelf: 'center',
53
+ bgcolor: '#FFFFFF'
54
+ },
55
+ src: favicon
56
+ }, /*#__PURE__*/React.createElement(ChromeIcon, {
57
+ sx: {
58
+ fontSize: '1rem',
59
+ cursor: 'grab',
60
+ color: 'primary.main',
61
+ alignSelf: 'center',
62
+ ':active': {
63
+ cursor: 'grabbing'
64
+ }
65
+ }
66
+ })), /*#__PURE__*/React.createElement(Box, null, mark === null || mark === void 0 || (_mark$attrs6 = mark.attrs) === null || _mark$attrs6 === void 0 ? void 0 : _mark$attrs6.title));
39
67
  };
40
68
  export default LinkViewWrapper;
package/dist/index.css CHANGED
@@ -87,50 +87,83 @@
87
87
  /* list */
88
88
  .tiptap.ProseMirror ol,
89
89
  .tiptap.ProseMirror ul {
90
- padding: 0 32px;
90
+ padding: 0 32px 0 24px;
91
91
  margin: 8px 0;
92
92
  list-style: none;
93
+ position: relative;
94
+ counter-reset: list-counter;
93
95
  }
94
96
 
95
97
  .tiptap.ProseMirror li {
98
+ display: flex;
96
99
  margin: 8px 0;
100
+ flex-direction: column;
101
+ position: relative;
97
102
  }
98
103
 
99
- .tiptap.ProseMirror ol ol ol ol {
100
- list-style: decimal;
104
+ .tiptap.ProseMirror ol li {
105
+ counter-increment: list-counter;
101
106
  }
102
107
 
103
- .tiptap.ProseMirror ol ol ol {
104
- list-style: lower-roman;
108
+ .tiptap.ProseMirror ol li::before {
109
+ position: absolute;
110
+ top: 0;
111
+ left: -24px;
112
+ content: counter(list-counter) '.' !important;
113
+ font-size: 16px;
114
+ line-height: 26px;
115
+ color: var(--mui-palette-primary-main);
105
116
  }
106
117
 
107
118
  .tiptap.ProseMirror ol ol {
108
- list-style: lower-alpha;
119
+ counter-reset: list-counter;
120
+ margin: 0;
121
+ }
122
+
123
+ .tiptap.ProseMirror ol ol li::before {
124
+ content: counter(list-counter, lower-alpha) '.' !important;
125
+ }
126
+
127
+ .tiptap.ProseMirror ol ol ol {
128
+ counter-reset: list-counter;
109
129
  }
110
130
 
111
- .tiptap.ProseMirror ol {
112
- list-style: decimal;
131
+ .tiptap.ProseMirror ol ol ol li::before {
132
+ content: counter(list-counter, lower-roman) '.' !important;
113
133
  }
114
134
 
115
- .tiptap.ProseMirror ul:not([data-type='taskList']) {
116
- list-style: disc;
135
+ .tiptap.ProseMirror ol ol ol ol {
136
+ counter-reset: list-counter;
137
+ }
138
+
139
+ .tiptap.ProseMirror ol ol ol ol li::before {
140
+ content: counter(list-counter) '.' !important;
141
+ }
142
+
143
+ .tiptap.ProseMirror ul:not([data-type='taskList']) li::before {
144
+ content: '•' !important;
145
+ position: absolute;
146
+ top: 0;
147
+ left: -24px;
148
+ font-size: 16px;
149
+ line-height: 26px;
150
+ color: var(--mui-palette-primary-main);
117
151
  }
118
152
 
119
153
  .tiptap.ProseMirror ul:not([data-type='taskList']) ul {
120
- list-style: circle;
154
+ margin: 0;
121
155
  }
122
156
 
123
- .tiptap.ProseMirror ul:not([data-type='taskList']) ul ul {
124
- list-style: square;
157
+ .tiptap.ProseMirror ul:not([data-type='taskList']) ul li::before {
158
+ content: '◦' !important;
125
159
  }
126
160
 
127
- .tiptap.ProseMirror ul:not([data-type='taskList']) ul ul ul {
128
- list-style: disc;
161
+ .tiptap.ProseMirror ul:not([data-type='taskList']) ul ul li::before {
162
+ content: '▪' !important;
129
163
  }
130
164
 
131
- .tiptap.ProseMirror ul:not([data-type='taskList']) li {
132
- position: relative;
133
- padding-left: 0;
165
+ .tiptap.ProseMirror ul:not([data-type='taskList']) ul ul ul li::before {
166
+ content: '•' !important;
134
167
  }
135
168
 
136
169
  .tiptap.ProseMirror ul[data-type='taskList'] {
@@ -176,12 +209,6 @@
176
209
  margin: 0;
177
210
  }
178
211
 
179
- .tiptap.ProseMirror li::marker {
180
- font-size: 16px;
181
- line-height: 26px;
182
- color: var(--mui-palette-primary-main);
183
- }
184
-
185
212
  /* code */
186
213
  .tiptap.ProseMirror .codeblock-wrapper {
187
214
  margin: 20px 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ctzhian/tiptap",
3
- "version": "1.12.8",
3
+ "version": "1.12.10",
4
4
  "description": "基于 Tiptap 二次开发的编辑器组件",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",