@difizen/libro-core 1.0.2 → 1.0.3

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 (38) hide show
  1. package/es/cell/libro-executable-cell-view.d.ts +4 -3
  2. package/es/cell/libro-executable-cell-view.d.ts.map +1 -1
  3. package/es/cell/libro-executable-cell-view.js +17 -2
  4. package/es/index.less +1 -0
  5. package/es/libro-model.d.ts +2 -1
  6. package/es/libro-model.d.ts.map +1 -1
  7. package/es/libro-model.js +53 -45
  8. package/es/libro-protocol.d.ts +2 -0
  9. package/es/libro-protocol.d.ts.map +1 -1
  10. package/es/libro-setting-contribution.d.ts.map +1 -1
  11. package/es/libro-setting-contribution.js +2 -2
  12. package/es/libro-setting.d.ts +1 -0
  13. package/es/libro-setting.d.ts.map +1 -1
  14. package/es/libro-setting.js +10 -0
  15. package/es/libro-view.d.ts +10 -0
  16. package/es/libro-view.d.ts.map +1 -1
  17. package/es/libro-view.js +46 -12
  18. package/es/output/output-area.d.ts +3 -1
  19. package/es/output/output-area.d.ts.map +1 -1
  20. package/es/output/output-area.js +83 -30
  21. package/es/output/output-model.d.ts +3 -1
  22. package/es/output/output-model.d.ts.map +1 -1
  23. package/es/output/output-model.js +11 -1
  24. package/es/output/output-protocol.d.ts +5 -2
  25. package/es/output/output-protocol.d.ts.map +1 -1
  26. package/es/toolbar/index.less +10 -10
  27. package/package.json +5 -5
  28. package/src/cell/libro-executable-cell-view.ts +25 -2
  29. package/src/index.less +1 -0
  30. package/src/libro-model.ts +8 -5
  31. package/src/libro-protocol.ts +3 -0
  32. package/src/libro-setting-contribution.ts +2 -0
  33. package/src/libro-setting.ts +11 -0
  34. package/src/libro-view.tsx +61 -10
  35. package/src/output/output-area.tsx +80 -21
  36. package/src/output/output-model.tsx +11 -1
  37. package/src/output/output-protocol.ts +8 -2
  38. package/src/toolbar/index.less +10 -10
package/src/index.less CHANGED
@@ -784,6 +784,7 @@
784
784
  font-size: 14px;
785
785
  font-family: PingFangSC;
786
786
  line-height: 22px;
787
+ white-space: nowrap;
787
788
  }
788
789
 
789
790
  .libro-tooltip-keybind {
@@ -4,16 +4,16 @@ import type {
4
4
  INotebookContent,
5
5
  INotebookMetadata,
6
6
  } from '@difizen/libro-common';
7
+ import { getOrigin, ConfigurationService } from '@difizen/libro-common/app';
8
+ import { Emitter } from '@difizen/libro-common/app';
9
+ import { prop } from '@difizen/libro-common/app';
10
+ import { inject, transient } from '@difizen/libro-common/app';
11
+ import { createMutex, YNotebook } from '@difizen/libro-shared-model';
7
12
  import type {
8
13
  CellTypeAdaptor,
9
14
  ISharedNotebook,
10
15
  NotebookChange,
11
16
  } from '@difizen/libro-shared-model';
12
- import { createMutex, YNotebook } from '@difizen/libro-shared-model';
13
- import { getOrigin, ConfigurationService } from '@difizen/libro-common/app';
14
- import { Emitter } from '@difizen/libro-common/app';
15
- import { prop } from '@difizen/libro-common/app';
16
- import { inject, transient } from '@difizen/libro-common/app';
17
17
  import { v4 } from 'uuid';
18
18
 
19
19
  import { LibroContentService } from './content/index.js';
@@ -108,6 +108,9 @@ export class LibroModel implements NotebookModel, DndListModel {
108
108
  @prop()
109
109
  cellsEditable = true;
110
110
 
111
+ @prop()
112
+ noEditorMode = false;
113
+
111
114
  get executable() {
112
115
  return this.outputEditable && this.runnable;
113
116
  }
@@ -88,6 +88,7 @@ export interface BaseNotebookModel {
88
88
  active?: CellView | undefined;
89
89
  activeIndex: number;
90
90
  dndAreaNullEnable: boolean;
91
+ noEditorMode: boolean;
91
92
 
92
93
  /**
93
94
  * Controlling whether the cell input is editable
@@ -280,6 +281,8 @@ export interface CellView extends View {
280
281
 
281
282
  editorStatus?: EditorStatus;
282
283
 
284
+ isLargeOutputDisplay?: boolean;
285
+
283
286
  calcEditorAreaHeight?: () => number;
284
287
 
285
288
  calcEditorOffset?: () => number;
@@ -7,6 +7,7 @@ import {
7
7
  CollapserClickActive,
8
8
  EnterEditModeWhenAddCell,
9
9
  HeaderToolbarVisible,
10
+ LargeOutputDisplay,
10
11
  MultiSelectionWhenShiftClick,
11
12
  RightContentFixed,
12
13
  } from './libro-setting.js';
@@ -23,6 +24,7 @@ export class LibroSettingContribution implements ConfigurationContribution {
23
24
  CollapserClickActive,
24
25
  MultiSelectionWhenShiftClick,
25
26
  RightContentFixed,
27
+ LargeOutputDisplay,
26
28
  ];
27
29
  }
28
30
  }
@@ -119,3 +119,14 @@ export const SpmReporter: ConfigurationNode<boolean> = {
119
119
  type: 'boolean',
120
120
  },
121
121
  };
122
+
123
+ export const LargeOutputDisplay: ConfigurationNode<boolean> = {
124
+ id: 'libro.output.large.display',
125
+ description: l10n.t('是否优化长文本 output 显示'),
126
+ title: 'Output',
127
+ type: 'checkbox',
128
+ defaultValue: true,
129
+ schema: {
130
+ type: 'boolean',
131
+ },
132
+ };
@@ -1,5 +1,6 @@
1
1
  import { ToTopOutlined } from '@ant-design/icons';
2
2
  import type { IModelContentChange } from '@difizen/libro-code-editor';
3
+ import type { MultilineString } from '@difizen/libro-common';
3
4
  import {
4
5
  concatMultilineString,
5
6
  copy2clipboard,
@@ -386,6 +387,14 @@ export class LibroView extends BaseView implements NotebookView {
386
387
  return this.cellScrollEmitter.event;
387
388
  }
388
389
 
390
+ outputRenderTabEmitter = new Emitter<{
391
+ mimeType: string;
392
+ data: MultilineString | null;
393
+ }>();
394
+ get onOutputRenderTab() {
395
+ return this.outputRenderTabEmitter.event;
396
+ }
397
+
389
398
  protected initializedDefer = new Deferred<void>();
390
399
 
391
400
  get initialized() {
@@ -432,6 +441,12 @@ export class LibroView extends BaseView implements NotebookView {
432
441
  async initialize() {
433
442
  this.model.isInitialized = false;
434
443
  const options = await this.model.initialize();
444
+ if (
445
+ options.length > 30 ||
446
+ (this.model.options['fileSize'] || 0) / (1024 * 1024) >= 5
447
+ ) {
448
+ this.addLargeFileWarning();
449
+ }
435
450
  this.configurationService
436
451
  .get(AutoInsertWhenNoCell)
437
452
  .then((value) => {
@@ -477,6 +492,48 @@ export class LibroView extends BaseView implements NotebookView {
477
492
 
478
493
  override view = LibroRender;
479
494
 
495
+ addLargeFileWarning = () => {
496
+ const skipWarning =
497
+ localStorage.getItem(
498
+ 'largefile' + `${this.options.modelId || this.options.id}`,
499
+ ) === 'true';
500
+ if (!skipWarning) {
501
+ const warningBox = document.createElement('div');
502
+ warningBox.style.position = 'fixed';
503
+ warningBox.style.top = '20px';
504
+ warningBox.style.left = '50%';
505
+ warningBox.style.display = 'flex';
506
+ warningBox.style.alignItems = 'center';
507
+ warningBox.style.color = '#000000E0';
508
+ warningBox.style.transform = 'translateX(-50%)';
509
+ warningBox.style.fontSize = '14px';
510
+ warningBox.style.background = '#ffffff';
511
+ warningBox.style.padding = '9px 12px';
512
+ warningBox.style.zIndex = '9999';
513
+ warningBox.style.borderRadius = '8px';
514
+ warningBox.style.boxShadow =
515
+ '0 6px 16px 0 rgba(0, 0, 0, 0.08),0 3px 6px -4px rgba(0, 0, 0, 0.12),0 9px 28px 8px rgba(0, 0, 0, 0.05)';
516
+ warningBox.innerHTML = `
517
+ <svg style="display: inline-block; margin-right: 8px" viewBox="64 64 896 896" focusable="false" data-icon="exclamation-circle" width="1em" height="1em" fill="#faad14" aria-hidden="true" data-macaca-recorder-select-tag="true"><path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm-32 232c0-4.4 3.6-8 8-8h48c4.4 0 8 3.6 8 8v272c0 4.4-3.6 8-8 8h-48c-4.4 0-8-3.6-8-8V296zm32 440a48.01 48.01 0 010-96 48.01 48.01 0 010 96z"></path></svg>
518
+ <span style ="line-height: 32px">即将打开的文件(内容过大 / cell 过多),可能会导致操作卡顿,请耐心等待~</span>
519
+ <a style="margin-left: 8px; color: #1677ff;">我知道了</a>
520
+ `;
521
+ warningBox.querySelector('a')?.addEventListener('click', () => {
522
+ // 将用户偏好保存到 localStorage
523
+ localStorage.setItem(
524
+ 'largefile' + `${this.options.modelId || this.options.id}`,
525
+ 'true',
526
+ );
527
+ document.body.removeChild(warningBox);
528
+ });
529
+ document.body.appendChild(warningBox);
530
+ // 可选:自动移除提示框
531
+ setTimeout(() => {
532
+ document.body.removeChild(warningBox);
533
+ }, 2000);
534
+ }
535
+ };
536
+
480
537
  override onViewMount = () => {
481
538
  this.libroService.active = this;
482
539
  this.libroSlotManager.setup(this);
@@ -487,17 +544,11 @@ export class LibroView extends BaseView implements NotebookView {
487
544
  });
488
545
  }
489
546
  if (
490
- this.model.cells.length > 30 ||
491
- (this.options['fileSize'] || 0) / (1024 * 1024) >= 5
547
+ this.model.isInitialized &&
548
+ (this.model.cells.length > 30 ||
549
+ (this.model.options['fileSize'] || 0) / (1024 * 1024) >= 5)
492
550
  ) {
493
- message.warning(
494
- <div>
495
- {l10n.t(
496
- '即将打开的文件(内容过大 / cell 过多),可能会导致操作卡顿,请耐心等待~',
497
- )}
498
- <Button type="link">{l10n.t('我知道了')}</Button>
499
- </div>,
500
- );
551
+ this.addLargeFileWarning();
501
552
  }
502
553
  // this.libroService.libroPerformanceStatistics.setRenderEnd(new Date());
503
554
 
@@ -1,4 +1,6 @@
1
- import type { IOutput } from '@difizen/libro-common';
1
+ import type { IOutput, JSONObject } from '@difizen/libro-common';
2
+ import { concatMultilineString } from '@difizen/libro-common';
3
+ import { getBundleOptions } from '@difizen/libro-common';
2
4
  import { isError, isStream } from '@difizen/libro-common';
3
5
  import type { ViewComponent, Contribution } from '@difizen/libro-common/app';
4
6
  import {
@@ -13,7 +15,6 @@ import {
13
15
  } from '@difizen/libro-common/app';
14
16
  import { Emitter, prop, contrib, inject, transient } from '@difizen/libro-common/app';
15
17
  import { useEffect, forwardRef } from 'react';
16
- import { v4 } from 'uuid';
17
18
 
18
19
  import { ExecutableCellView } from '../cell/index.js';
19
20
  import type { CellView } from '../libro-protocol.js';
@@ -28,7 +29,6 @@ import { OutputContribution } from './output-protocol.js';
28
29
  const LibroOutputAreaRender = forwardRef<HTMLDivElement>(
29
30
  function LibroOutputAreaRender(props, ref) {
30
31
  const outputArea = useInject<LibroOutputArea>(ViewInstance);
31
-
32
32
  // const cellModel = outputArea.cell.model;
33
33
  // const executing = ExecutableCellModel.is(cellModel) && cellModel.executing;
34
34
 
@@ -72,8 +72,6 @@ export class LibroOutputArea extends BaseView implements BaseOutputArea {
72
72
  @prop()
73
73
  outputs: BaseOutputView[] = [];
74
74
 
75
- // lastOutputContainerHeight?: number;
76
-
77
75
  constructor(@inject(ViewOption) option: IOutputAreaOption) {
78
76
  super();
79
77
  this.cell = option.cell;
@@ -86,6 +84,8 @@ export class LibroOutputArea extends BaseView implements BaseOutputArea {
86
84
 
87
85
  protected clearNext = false;
88
86
  protected lastStream = '';
87
+ protected lastDisplayStream: string[] = [];
88
+ protected isFirstLastOverHeight = true;
89
89
 
90
90
  protected lastName?: 'stdout' | 'stderr';
91
91
 
@@ -108,7 +108,7 @@ export class LibroOutputArea extends BaseView implements BaseOutputArea {
108
108
  const provider = this.findProvider(options);
109
109
 
110
110
  return provider.factory({
111
- output: { _librOutputId: v4(), ...options },
111
+ output: options,
112
112
  trusted: this.cell.model.trusted,
113
113
  cell: this.cell,
114
114
  });
@@ -131,26 +131,56 @@ export class LibroOutputArea extends BaseView implements BaseOutputArea {
131
131
  // In order to get a list change event, we add the previous
132
132
  // text to the current item and replace the previous item.
133
133
  // This also replaces the metadata of the last item.
134
- this.lastStream += normalize(output.text);
134
+ const normalizeOutput = normalize(output.text);
135
+ this.lastStream += normalizeOutput;
135
136
  this.lastStream = removeOverwrittenChars(this.lastStream);
136
137
  output.text = this.lastStream;
137
138
  const index = this.length - 1;
138
- this.set(index, output);
139
+ const outputModel = this.outputs[index];
140
+ const containerRec = outputModel.container?.current?.getBoundingClientRect();
141
+
142
+ if ((containerRec?.height || 0) > 240) {
143
+ if (this.isFirstLastOverHeight) {
144
+ this.lastDisplayStream = [concatMultilineString(this.lastStream)];
145
+ this.lastDisplayStream.push('\n...\n');
146
+ this.isFirstLastOverHeight = false;
147
+ } else {
148
+ this.lastDisplayStream = [
149
+ ...this.lastDisplayStream,
150
+ ...splitWithNewline(normalizeOutput), //考虑有可能一次性输出多个带换行符的内容
151
+ ];
152
+ }
153
+ output['is_over_height'] = true;
154
+ output['display_text'] = this.lastDisplayStream;
155
+ }
156
+ this.set(index, output, true);
139
157
  return this.length;
140
158
  }
141
-
159
+ this.isFirstLastOverHeight = true;
142
160
  if (isStream(output)) {
143
- output.text = removeOverwrittenChars(normalize(output.text));
161
+ const text = removeOverwrittenChars(normalize(output.text));
162
+ output.text = text;
163
+ const split_text = splitWithNewline(text);
164
+ if (this.isFirstLastOverHeight && split_text.length > 15) {
165
+ this.lastDisplayStream = [concatMultilineString(split_text.slice(0, 15))];
166
+ this.lastDisplayStream.push('\n...\n');
167
+ this.lastDisplayStream = [...this.lastDisplayStream, ...split_text.slice(15)];
168
+ this.isFirstLastOverHeight = false;
169
+ output['is_over_height'] = true;
170
+ output['display_text'] = this.lastDisplayStream;
171
+ }
144
172
  }
145
-
146
173
  const outputModel = this.doCreateOutput(output);
147
174
 
148
175
  // Update the stream information.
149
176
  if (isStream(output)) {
150
- this.lastStream = normalize(output.text);
177
+ const normalizedOutput = normalize(output.text);
178
+ this.lastStream = normalizedOutput;
151
179
  this.lastName = output.name;
152
180
  } else {
153
181
  this.lastStream = '';
182
+ this.lastDisplayStream = [];
183
+ this.isFirstLastOverHeight = true;
154
184
  }
155
185
  const model = await outputModel;
156
186
  model.onDisposed(() => {
@@ -165,18 +195,29 @@ export class LibroOutputArea extends BaseView implements BaseOutputArea {
165
195
  this.outputs = outputs;
166
196
  }
167
197
 
168
- set = async (index: number, output: IOutput) => {
169
- const outputModel = this.doCreateOutput(output);
170
- const current = this.outputs[index];
171
- current.dispose();
172
- const model = await outputModel;
173
- model.onDisposed(() => {
174
- this.remove(model);
175
- });
176
- this.outputs[index] = model;
198
+ set = async (index: number, output: IOutput, shouldReplace?: boolean) => {
199
+ if (shouldReplace) {
200
+ const outputModel = this.outputs[index];
201
+ const { data } = getBundleOptions(output);
202
+ outputModel.raw = output;
203
+ outputModel.data = data as JSONObject;
204
+ outputModel.onUpdateEmitter.fire();
205
+ // outputModel.data
206
+ } else {
207
+ const outputModel = this.doCreateOutput(output);
208
+ const current = this.outputs[index];
209
+ current.dispose();
210
+ const model = await outputModel;
211
+ model.onDisposed(() => {
212
+ this.remove(model);
213
+ });
214
+ this.outputs[index] = model;
215
+ }
177
216
  };
178
217
  clear(wait?: boolean | undefined) {
179
218
  this.lastStream = '';
219
+ this.lastDisplayStream = [];
220
+ this.isFirstLastOverHeight = true;
180
221
  if (wait) {
181
222
  this.clearNext = true;
182
223
  return;
@@ -264,6 +305,24 @@ export function removeOverwrittenChars(text: string): string {
264
305
  return fixCarriageReturn(fixBackspace(text));
265
306
  }
266
307
 
308
+ function splitWithNewline(input: string): string[] {
309
+ const parts = input.split(/(\n)/);
310
+ const result: string[] = [];
311
+
312
+ for (let i = 0; i < parts.length; i += 2) {
313
+ const text = parts[i];
314
+ const newline = parts[i + 1] || '';
315
+ const combined = text + newline;
316
+
317
+ // 仅保留非空项
318
+ if (text || newline) {
319
+ result.push(combined);
320
+ }
321
+ }
322
+
323
+ return result;
324
+ }
325
+
267
326
  /**
268
327
  * Normalize an output.
269
328
  */
@@ -1,5 +1,5 @@
1
1
  import type { IOutput, JSONObject, PartialJSONObject } from '@difizen/libro-common';
2
- import { BaseView, prop, view, ViewOption } from '@difizen/libro-common/app';
2
+ import { BaseView, Emitter, prop, view, ViewOption } from '@difizen/libro-common/app';
3
3
  import { inject, transient } from '@difizen/libro-common/app';
4
4
  import type { FC } from 'react';
5
5
  import { v4 } from 'uuid';
@@ -32,6 +32,13 @@ export class LibroOutputView extends BaseView implements BaseOutputView {
32
32
 
33
33
  @prop()
34
34
  data: JSONObject;
35
+
36
+ onUpdateEmitter = new Emitter<void>();
37
+
38
+ get onUpdate() {
39
+ return this.onUpdateEmitter.event;
40
+ }
41
+
35
42
  @prop()
36
43
  metadata: PartialJSONObject;
37
44
  type: string;
@@ -44,6 +51,9 @@ export class LibroOutputView extends BaseView implements BaseOutputView {
44
51
  this.type = 'libro-default-output';
45
52
  this.data = {};
46
53
  this.metadata = {};
54
+ this.onUpdate(() => {
55
+ this.cell.parent.model.onChange?.();
56
+ });
47
57
  }
48
58
 
49
59
  render: FC<{ output: BaseOutputView }> = LibroOutputModelRender;
@@ -3,7 +3,7 @@ import type {
3
3
  ReadonlyPartialJSONObject,
4
4
  JSONObject,
5
5
  } from '@difizen/libro-common';
6
- import type { Event } from '@difizen/libro-common/app';
6
+ import type { Emitter, Event } from '@difizen/libro-common/app';
7
7
  import type { View } from '@difizen/libro-common/app';
8
8
  import { Syringe } from '@difizen/libro-common/app';
9
9
 
@@ -100,7 +100,13 @@ export interface BaseOutputView extends View {
100
100
  /**
101
101
  * The data associated with the model.
102
102
  */
103
- readonly data: JSONObject;
103
+ data: JSONObject;
104
+
105
+ onUpdateEmitter: Emitter<void>;
106
+
107
+ onUpdate: Event<void>;
108
+
109
+ isTabRender?: boolean;
104
110
 
105
111
  /**
106
112
  * The metadata associated with the model.
@@ -4,16 +4,16 @@
4
4
  .libro-hide-selector-menu {
5
5
  .@{ant-prefix}-dropdown-menu {
6
6
  background-color: var(--mana-libro-popover-background-color);
7
- }
8
- .@{ant-prefix}-dropdown-menu-item {
9
- color: var(--mana-libro-toolbar-menu-label-color);
10
- }
11
- .@{ant-prefix}-dropdown-menu-item-disabled {
12
- color: var(--mana-libro-toolbar-menu-disabled-label-color) !important;
13
- }
14
- .@{ant-prefix}-dropdown-menu-item.@{ant-prefix}-dropdown-menu-item-disabled:hover,
15
- .@{ant-prefix}-dropdown-menu-item:hover {
16
- background-color: var(--mana-libro-menu-hover-color);
7
+ .@{ant-prefix}-dropdown-menu-item {
8
+ color: var(--mana-libro-toolbar-menu-label-color);
9
+ }
10
+ .@{ant-prefix}-dropdown-menu-item-disabled {
11
+ color: var(--mana-libro-toolbar-menu-disabled-label-color);
12
+ }
13
+ .@{ant-prefix}-dropdown-menu-item.@{ant-prefix}-dropdown-menu-item-disabled:hover,
14
+ .@{ant-prefix}-dropdown-menu-item:hover {
15
+ background-color: var(--mana-libro-menu-hover-color);
16
+ }
17
17
  }
18
18
  }
19
19