bb-relay 0.0.31 → 0.0.32

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.
@@ -1,5 +1,4 @@
1
1
  export { P as PROJECT_CATEGORY } from './PROJECT_CATEGORY-BivLHtB6.mjs';
2
- export { C as COLOR_LIST } from './COLOR_LIST-BfSSBFUY.mjs';
3
2
 
4
3
  declare const SENTINEL_FILE = ".release.commit";
5
4
 
@@ -1,5 +1,4 @@
1
1
  export { P as PROJECT_CATEGORY } from './PROJECT_CATEGORY-BivLHtB6.js';
2
- export { C as COLOR_LIST } from './COLOR_LIST-BfSSBFUY.js';
3
2
 
4
3
  declare const SENTINEL_FILE = ".release.commit";
5
4
 
package/dist/constant.js CHANGED
@@ -45,35 +45,9 @@ var PROJECT_CATEGORY = [
45
45
  ];
46
46
  var PROJECT_CATEGORY_default = PROJECT_CATEGORY;
47
47
 
48
- // src/constants/COLOR_LIST.ts
49
- var COLOR_LIST = [
50
- "red",
51
- "slate",
52
- "stone",
53
- "zinc",
54
- "gray",
55
- "orange",
56
- "amber",
57
- "yellow",
58
- "lime",
59
- "green",
60
- "emerald",
61
- "teal",
62
- "cyan",
63
- "sky",
64
- "blue",
65
- "indigo",
66
- "violet",
67
- "purple",
68
- "fuchsia",
69
- "pink",
70
- "rose"
71
- ];
72
-
73
-
74
48
 
75
49
 
76
50
 
77
51
 
78
52
 
79
- exports.COLOR_LIST = COLOR_LIST; exports.DATA_ALIAS = DATA_ALIAS; exports.PLUGIN_URL = PLUGIN_URL_default; exports.PROJECT_CATEGORY = PROJECT_CATEGORY_default; exports.SENTINEL_FILE = SENTINEL_FILE_default; exports.WEBSITE = WEBSITE_default;
53
+ exports.DATA_ALIAS = DATA_ALIAS; exports.PLUGIN_URL = PLUGIN_URL_default; exports.PROJECT_CATEGORY = PROJECT_CATEGORY_default; exports.SENTINEL_FILE = SENTINEL_FILE_default; exports.WEBSITE = WEBSITE_default;
package/dist/constant.mjs CHANGED
@@ -44,33 +44,7 @@ var PROJECT_CATEGORY = [
44
44
  "Other"
45
45
  ];
46
46
  var PROJECT_CATEGORY_default = PROJECT_CATEGORY;
47
-
48
- // src/constants/COLOR_LIST.ts
49
- var COLOR_LIST = [
50
- "red",
51
- "slate",
52
- "stone",
53
- "zinc",
54
- "gray",
55
- "orange",
56
- "amber",
57
- "yellow",
58
- "lime",
59
- "green",
60
- "emerald",
61
- "teal",
62
- "cyan",
63
- "sky",
64
- "blue",
65
- "indigo",
66
- "violet",
67
- "purple",
68
- "fuchsia",
69
- "pink",
70
- "rose"
71
- ];
72
47
  export {
73
- COLOR_LIST,
74
48
  DATA_ALIAS,
75
49
  PLUGIN_URL_default as PLUGIN_URL,
76
50
  PROJECT_CATEGORY_default as PROJECT_CATEGORY,
package/dist/editor.d.mts CHANGED
@@ -2,7 +2,6 @@ export { C as Commit } from './Commit-PdsjrKSG.mjs';
2
2
  export { a as FileStat, F as FolderContent, b as FolderStat } from './FolderStat-Bhmwwc0t.mjs';
3
3
  export { F as FileContent } from './FileContent-BWulmcoi.mjs';
4
4
  import { L as Language, a as Locale } from './Locale-DvFwr_9k.mjs';
5
- import { a as Color } from './COLOR_LIST-BfSSBFUY.mjs';
6
5
  export { R as Result } from './Result-BLbZLEgX.mjs';
7
6
 
8
7
  type CopyArg = {
@@ -44,7 +43,6 @@ type Shortcut = {
44
43
  keys: string[];
45
44
  };
46
45
  type Settings = {
47
- theme: "light" | "dark" | "system";
48
46
  language?: Locale;
49
47
  sidebarWidth: number;
50
48
  baseAppFont: number;
@@ -53,7 +51,7 @@ type Settings = {
53
51
  showAllFile: boolean;
54
52
  trashBehaviour: "permanent" | "trash";
55
53
  shortcuts: Shortcut[];
56
- color: Color;
54
+ theme: string;
57
55
  /**
58
56
  * { [fileExtension: string]: pluginId }
59
57
  */
@@ -73,4 +71,4 @@ interface TutorialHeader {
73
71
 
74
72
  type WatchFSEvent = "updated" | "deleted" | "updated" | "add";
75
73
 
76
- export { Color, type CopyArg, type GitFileStatus, type IconArg, type MenuContent, type Nav, type Route, type Settings, type Shortcut, type TutorialHeader, type WatchFSEvent };
74
+ export type { CopyArg, GitFileStatus, IconArg, MenuContent, Nav, Route, Settings, Shortcut, TutorialHeader, WatchFSEvent };
package/dist/editor.d.ts CHANGED
@@ -2,7 +2,6 @@ export { C as Commit } from './Commit-PdsjrKSG.js';
2
2
  export { a as FileStat, F as FolderContent, b as FolderStat } from './FolderStat-Bhmwwc0t.js';
3
3
  export { F as FileContent } from './FileContent-BWulmcoi.js';
4
4
  import { L as Language, a as Locale } from './Locale-DvFwr_9k.js';
5
- import { a as Color } from './COLOR_LIST-BfSSBFUY.js';
6
5
  export { R as Result } from './Result-BLbZLEgX.js';
7
6
 
8
7
  type CopyArg = {
@@ -44,7 +43,6 @@ type Shortcut = {
44
43
  keys: string[];
45
44
  };
46
45
  type Settings = {
47
- theme: "light" | "dark" | "system";
48
46
  language?: Locale;
49
47
  sidebarWidth: number;
50
48
  baseAppFont: number;
@@ -53,7 +51,7 @@ type Settings = {
53
51
  showAllFile: boolean;
54
52
  trashBehaviour: "permanent" | "trash";
55
53
  shortcuts: Shortcut[];
56
- color: Color;
54
+ theme: string;
57
55
  /**
58
56
  * { [fileExtension: string]: pluginId }
59
57
  */
@@ -73,4 +71,4 @@ interface TutorialHeader {
73
71
 
74
72
  type WatchFSEvent = "updated" | "deleted" | "updated" | "add";
75
73
 
76
- export { Color, type CopyArg, type GitFileStatus, type IconArg, type MenuContent, type Nav, type Route, type Settings, type Shortcut, type TutorialHeader, type WatchFSEvent };
74
+ export type { CopyArg, GitFileStatus, IconArg, MenuContent, Nav, Route, Settings, Shortcut, TutorialHeader, WatchFSEvent };
package/dist/index.d.mts CHANGED
@@ -43,6 +43,10 @@ type Manifest = {
43
43
  author?: string;
44
44
  homepage?: string;
45
45
  repository?: string;
46
+ theme?: {
47
+ name: string;
48
+ path: string;
49
+ };
46
50
  [key: string]: unknown;
47
51
  };
48
52
 
package/dist/index.d.ts CHANGED
@@ -43,6 +43,10 @@ type Manifest = {
43
43
  author?: string;
44
44
  homepage?: string;
45
45
  repository?: string;
46
+ theme?: {
47
+ name: string;
48
+ path: string;
49
+ };
46
50
  [key: string]: unknown;
47
51
  };
48
52
 
package/dist/index.js CHANGED
@@ -115,6 +115,11 @@ var validateManifest = (manifestContent) => {
115
115
  errors.push("Manifest entry must be a js file");
116
116
  } else fileChecks.push(content.entry);
117
117
  }
118
+ if (content.theme) {
119
+ if (!content.theme.name) errors.push("No name for theme");
120
+ if (!content.theme.path) errors.push("No css file path provided");
121
+ else fileChecks.push(content.theme.path);
122
+ }
118
123
  return {
119
124
  errors,
120
125
  fileChecks,
package/dist/index.mjs CHANGED
@@ -115,6 +115,11 @@ var validateManifest = (manifestContent) => {
115
115
  errors.push("Manifest entry must be a js file");
116
116
  } else fileChecks.push(content.entry);
117
117
  }
118
+ if (content.theme) {
119
+ if (!content.theme.name) errors.push("No name for theme");
120
+ if (!content.theme.path) errors.push("No css file path provided");
121
+ else fileChecks.push(content.theme.path);
122
+ }
118
123
  return {
119
124
  errors,
120
125
  fileChecks,
package/dist/plugin.d.mts CHANGED
@@ -17,7 +17,8 @@ type BaseInstruction<T extends InstructionTypes> = {
17
17
  click?: string;
18
18
  change?: string;
19
19
  };
20
- children?: ElementInstruction | ElementInstruction[] | string;
20
+ children?: ElementInstruction | (ElementInstruction | null)[] | string | null;
21
+ scrollIntoView?: boolean;
21
22
  };
22
23
 
23
24
  interface ButtonInstruction extends BaseInstruction<"button"> {
@@ -50,7 +51,6 @@ interface InputInstruction extends BaseInstruction<"input"> {
50
51
  }
51
52
 
52
53
  interface ViewInstruction extends BaseInstruction<"view"> {
53
- children: ElementInstruction[];
54
54
  }
55
55
 
56
56
  interface MenuInstruction extends BaseInstruction<"menu"> {
@@ -150,25 +150,12 @@ type PluginElement = {
150
150
  container: ContainerElement;
151
151
  };
152
152
 
153
- type StatusElement = {
154
- type: "icon";
155
- icon: IconName;
156
- } | {
157
- type: "text";
158
- text: string;
159
- };
160
- type RenderStatusType = {
161
- element: StatusElement[];
162
- message: string;
163
- onClick: () => void;
164
- };
165
-
166
153
  type EventHandler<T = unknown> = (payload?: T) => unknown | Promise<unknown>;
167
154
 
168
155
  type Store<T extends Record<string, unknown>> = {
169
156
  get(): T;
170
157
  set(next: SetState<T>): void;
171
- subscribe(listener: (state: T) => void): () => void;
158
+ subscribe(listener: (state: T, prevState: T) => void): () => void;
172
159
  };
173
160
  type EditorAPI = {
174
161
  /**
@@ -185,7 +172,7 @@ type EditorAPI = {
185
172
  getFileStat: (path: string) => Promise<Result<FileStat>>;
186
173
  deleteFile: (path: string) => Promise<Result>;
187
174
  copyFile: (filePath: string, destination: string) => Promise<Result<string>>;
188
- listFiles: (folder: string, glob?: string) => Promise<Result<string[]>>;
175
+ listFiles: (glob?: string) => Promise<Result<string[]>>;
189
176
  };
190
177
  };
191
178
  type SetState<T extends Record<string, unknown>> = Partial<T> | ((arg: T) => T);
@@ -202,6 +189,7 @@ declare abstract class Plugin<T extends Record<string, unknown>> {
202
189
  private subscriptions;
203
190
  private subscribe;
204
191
  private sendMessage;
192
+ private eventIds;
205
193
  private index;
206
194
  /** App api, the ones available will be based on the permissions in the manifest*/
207
195
  protected editor: EditorAPI;
@@ -278,12 +266,9 @@ declare abstract class Plugin<T extends Record<string, unknown>> {
278
266
  protected on(event: "file-created", callback: (filePath: string) => void): void;
279
267
  protected on(event: "file-changed", callback: (filePath: string) => void): void;
280
268
  protected on(event: "file-deleted", callback: (filePath: string) => void): void;
281
- abstract renderStatusBar(): RenderStatusType | null;
282
- private registerStatusBar;
283
- private onStatusLoad;
284
269
  protected setState(param: SetState<T>): void;
285
270
  protected getState(): T;
286
- protected notify(message: string): void;
271
+ selectFile(filePath: string): void;
287
272
  }
288
273
 
289
274
  type Message = {
@@ -294,4 +279,17 @@ type Message = {
294
279
 
295
280
  declare const getPluginChannel: (event: string, pluginId: string) => string;
296
281
 
282
+ type StatusElement = {
283
+ type: "icon";
284
+ icon: IconName;
285
+ } | {
286
+ type: "text";
287
+ text: string;
288
+ };
289
+ type RenderStatusType = {
290
+ element: StatusElement[];
291
+ message: string;
292
+ onClick: () => void;
293
+ };
294
+
297
295
  export { type EditorAPI, type ElementInstruction, type InputInstruction, type Message, type PluginElement, type RenderStatusType, type SetState, type Store, type TextVariant, type TextareaInstruction, Plugin as default, getPluginChannel };
package/dist/plugin.d.ts CHANGED
@@ -17,7 +17,8 @@ type BaseInstruction<T extends InstructionTypes> = {
17
17
  click?: string;
18
18
  change?: string;
19
19
  };
20
- children?: ElementInstruction | ElementInstruction[] | string;
20
+ children?: ElementInstruction | (ElementInstruction | null)[] | string | null;
21
+ scrollIntoView?: boolean;
21
22
  };
22
23
 
23
24
  interface ButtonInstruction extends BaseInstruction<"button"> {
@@ -50,7 +51,6 @@ interface InputInstruction extends BaseInstruction<"input"> {
50
51
  }
51
52
 
52
53
  interface ViewInstruction extends BaseInstruction<"view"> {
53
- children: ElementInstruction[];
54
54
  }
55
55
 
56
56
  interface MenuInstruction extends BaseInstruction<"menu"> {
@@ -150,25 +150,12 @@ type PluginElement = {
150
150
  container: ContainerElement;
151
151
  };
152
152
 
153
- type StatusElement = {
154
- type: "icon";
155
- icon: IconName;
156
- } | {
157
- type: "text";
158
- text: string;
159
- };
160
- type RenderStatusType = {
161
- element: StatusElement[];
162
- message: string;
163
- onClick: () => void;
164
- };
165
-
166
153
  type EventHandler<T = unknown> = (payload?: T) => unknown | Promise<unknown>;
167
154
 
168
155
  type Store<T extends Record<string, unknown>> = {
169
156
  get(): T;
170
157
  set(next: SetState<T>): void;
171
- subscribe(listener: (state: T) => void): () => void;
158
+ subscribe(listener: (state: T, prevState: T) => void): () => void;
172
159
  };
173
160
  type EditorAPI = {
174
161
  /**
@@ -185,7 +172,7 @@ type EditorAPI = {
185
172
  getFileStat: (path: string) => Promise<Result<FileStat>>;
186
173
  deleteFile: (path: string) => Promise<Result>;
187
174
  copyFile: (filePath: string, destination: string) => Promise<Result<string>>;
188
- listFiles: (folder: string, glob?: string) => Promise<Result<string[]>>;
175
+ listFiles: (glob?: string) => Promise<Result<string[]>>;
189
176
  };
190
177
  };
191
178
  type SetState<T extends Record<string, unknown>> = Partial<T> | ((arg: T) => T);
@@ -202,6 +189,7 @@ declare abstract class Plugin<T extends Record<string, unknown>> {
202
189
  private subscriptions;
203
190
  private subscribe;
204
191
  private sendMessage;
192
+ private eventIds;
205
193
  private index;
206
194
  /** App api, the ones available will be based on the permissions in the manifest*/
207
195
  protected editor: EditorAPI;
@@ -278,12 +266,9 @@ declare abstract class Plugin<T extends Record<string, unknown>> {
278
266
  protected on(event: "file-created", callback: (filePath: string) => void): void;
279
267
  protected on(event: "file-changed", callback: (filePath: string) => void): void;
280
268
  protected on(event: "file-deleted", callback: (filePath: string) => void): void;
281
- abstract renderStatusBar(): RenderStatusType | null;
282
- private registerStatusBar;
283
- private onStatusLoad;
284
269
  protected setState(param: SetState<T>): void;
285
270
  protected getState(): T;
286
- protected notify(message: string): void;
271
+ selectFile(filePath: string): void;
287
272
  }
288
273
 
289
274
  type Message = {
@@ -294,4 +279,17 @@ type Message = {
294
279
 
295
280
  declare const getPluginChannel: (event: string, pluginId: string) => string;
296
281
 
282
+ type StatusElement = {
283
+ type: "icon";
284
+ icon: IconName;
285
+ } | {
286
+ type: "text";
287
+ text: string;
288
+ };
289
+ type RenderStatusType = {
290
+ element: StatusElement[];
291
+ message: string;
292
+ onClick: () => void;
293
+ };
294
+
297
295
  export { type EditorAPI, type ElementInstruction, type InputInstruction, type Message, type PluginElement, type RenderStatusType, type SetState, type Store, type TextVariant, type TextareaInstruction, Plugin as default, getPluginChannel };
package/dist/plugin.js CHANGED
@@ -1,9 +1,10 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/lib/plugin/getPluginChannel.ts
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }// src/lib/plugin/getPluginChannel.ts
2
2
  var getPluginChannel = (event, pluginId) => {
3
3
  return `plugin:${pluginId}:${event}`;
4
4
  };
5
5
 
6
6
  // src/lib/plugin/index.ts
7
+ var _fastdeepequal = require('fast-deep-equal'); var _fastdeepequal2 = _interopRequireDefault(_fastdeepequal);
7
8
  var Plugin = class {
8
9
  /** Constructor */
9
10
  constructor({
@@ -19,7 +20,8 @@ var Plugin = class {
19
20
  this.registeredHandlers = /* @__PURE__ */ new Set();
20
21
  /** Listeners: ipc.on() */
21
22
  this.subscriptions = /* @__PURE__ */ new Map();
22
- this.index = {};
23
+ this.eventIds = [];
24
+ this.index = -1;
23
25
  /** App api, the ones available will be based on the permissions in the manifest*/
24
26
  this.editor = {};
25
27
  this.element = {
@@ -45,29 +47,40 @@ var Plugin = class {
45
47
  this.editor = editor;
46
48
  this.store = store;
47
49
  const value = store.get();
48
- store.set({ ...this.init(), ...value });
50
+ this.setState({ ...this.init(), ...value });
51
+ this.store.subscribe((curr, prev) => {
52
+ const isEqual = _fastdeepequal2.default.call(void 0, curr, prev);
53
+ if (isEqual) return;
54
+ this.index = -1;
55
+ this.eventIds.forEach((event) => this.off(event));
56
+ this.eventIds = [];
57
+ const sidebar = this.renderSidebar();
58
+ if (sidebar) this.render("sidebar", sidebar);
59
+ });
49
60
  this.onSidebarLoad();
50
- this.onStatusLoad();
51
61
  }
52
62
  // -----------------------------
53
63
  // Element Factory
54
64
  // -----------------------------
55
65
  makeElement(construct, props) {
56
66
  const { onClick, onChange, key, ...rest } = props;
57
- const elementIndex = this.index[construct] || 0;
58
- const id = _nullishCoalesce(key, () => ( construct + elementIndex));
59
- this.index[construct] = elementIndex + 1;
60
- if (onClick) this.registerSubscription(id, onClick);
67
+ const id = _nullishCoalesce(key, () => ( construct + this.index));
68
+ this.index += 1;
69
+ const eventId = crypto.randomUUID();
70
+ const clickId = `click:${eventId}`;
71
+ const changeId = `change:${eventId}`;
72
+ this.eventIds.push(...[clickId, changeId]);
73
+ if (onClick) this.registerSubscription(clickId, onClick);
61
74
  if (onChange) {
62
- this.registerSubscription(id, (v) => onChange(v));
75
+ this.registerSubscription(changeId, (v) => onChange(v));
63
76
  }
64
77
  return {
65
78
  ...rest,
66
79
  key: id,
67
80
  construct,
68
81
  action: {
69
- click: onClick ? this.getPluginChannel(id) : void 0,
70
- change: onChange ? this.getPluginChannel(id) : void 0
82
+ click: onClick ? this.getPluginChannel(clickId) : void 0,
83
+ change: onChange ? this.getPluginChannel(changeId) : void 0
71
84
  }
72
85
  };
73
86
  }
@@ -75,7 +88,7 @@ var Plugin = class {
75
88
  this.sendMessage({ type: message, arg });
76
89
  }
77
90
  onSidebarLoad() {
78
- this.index = {};
91
+ this.index = -1;
79
92
  this.registerHandler("sidebar-load", () => {
80
93
  return this.renderSidebar();
81
94
  });
@@ -150,33 +163,33 @@ var Plugin = class {
150
163
  on(event, callback) {
151
164
  this.registerSubscription(event, callback);
152
165
  }
153
- registerStatusBar() {
154
- const arg = this.renderStatusBar();
155
- if (!arg) return;
156
- const { onClick, ...rest } = arg;
157
- if (arg) {
158
- this.sendMessage({
159
- type: `status`,
160
- arg: rest
161
- });
162
- this.off("status-click");
163
- this.registerSubscription("status-click", onClick);
164
- }
165
- }
166
- onStatusLoad() {
167
- this.registerHandler("status-load", () => {
168
- return this.renderStatusBar();
169
- });
170
- }
166
+ // -----------------------------
167
+ // Status Bar
168
+ // -----------------------------
169
+ // abstract renderStatusBar(): RenderStatusType | null;
170
+ // private registerStatusBar(): void {
171
+ // const arg = this.renderStatusBar();
172
+ // if (!arg) return;
173
+ // const { onClick, ...rest } = arg;
174
+ // if (arg) {
175
+ // this.sendMessage({
176
+ // type: `status`,
177
+ // arg: rest,
178
+ // });
179
+ // this.off("status-click");
180
+ // this.registerSubscription("status-click", onClick);
181
+ // }
182
+ // }
183
+ // private onStatusLoad(): void {
184
+ // this.registerHandler("status-load", () => {
185
+ // return this.renderStatusBar();
186
+ // });
187
+ // }
171
188
  // -----------------------------
172
189
  // State
173
190
  // -----------------------------
174
191
  setState(param) {
175
- this.index = {};
176
192
  this.store.set(param);
177
- const sidebar = this.renderSidebar();
178
- if (sidebar) this.render("sidebar", sidebar);
179
- this.registerStatusBar();
180
193
  }
181
194
  getState() {
182
195
  return this.store.get();
@@ -184,10 +197,21 @@ var Plugin = class {
184
197
  // -----------------------------
185
198
  // Logging
186
199
  // -----------------------------
187
- notify(message) {
200
+ // protected notify(message: string): void {
201
+ // this.sendMessage({
202
+ // type: "notify",
203
+ // arg: message,
204
+ // });
205
+ // }
206
+ // -----------------------------
207
+ // Utils
208
+ // -----------------------------
209
+ selectFile(filePath) {
188
210
  this.sendMessage({
189
- type: "notify",
190
- arg: message
211
+ type: "select-file",
212
+ arg: {
213
+ filePath
214
+ }
191
215
  });
192
216
  }
193
217
  };
package/dist/plugin.mjs CHANGED
@@ -4,6 +4,7 @@ var getPluginChannel = (event, pluginId) => {
4
4
  };
5
5
 
6
6
  // src/lib/plugin/index.ts
7
+ import equal from "fast-deep-equal";
7
8
  var Plugin = class {
8
9
  /** Constructor */
9
10
  constructor({
@@ -19,7 +20,8 @@ var Plugin = class {
19
20
  this.registeredHandlers = /* @__PURE__ */ new Set();
20
21
  /** Listeners: ipc.on() */
21
22
  this.subscriptions = /* @__PURE__ */ new Map();
22
- this.index = {};
23
+ this.eventIds = [];
24
+ this.index = -1;
23
25
  /** App api, the ones available will be based on the permissions in the manifest*/
24
26
  this.editor = {};
25
27
  this.element = {
@@ -45,29 +47,40 @@ var Plugin = class {
45
47
  this.editor = editor;
46
48
  this.store = store;
47
49
  const value = store.get();
48
- store.set({ ...this.init(), ...value });
50
+ this.setState({ ...this.init(), ...value });
51
+ this.store.subscribe((curr, prev) => {
52
+ const isEqual = equal(curr, prev);
53
+ if (isEqual) return;
54
+ this.index = -1;
55
+ this.eventIds.forEach((event) => this.off(event));
56
+ this.eventIds = [];
57
+ const sidebar = this.renderSidebar();
58
+ if (sidebar) this.render("sidebar", sidebar);
59
+ });
49
60
  this.onSidebarLoad();
50
- this.onStatusLoad();
51
61
  }
52
62
  // -----------------------------
53
63
  // Element Factory
54
64
  // -----------------------------
55
65
  makeElement(construct, props) {
56
66
  const { onClick, onChange, key, ...rest } = props;
57
- const elementIndex = this.index[construct] || 0;
58
- const id = key ?? construct + elementIndex;
59
- this.index[construct] = elementIndex + 1;
60
- if (onClick) this.registerSubscription(id, onClick);
67
+ const id = key ?? construct + this.index;
68
+ this.index += 1;
69
+ const eventId = crypto.randomUUID();
70
+ const clickId = `click:${eventId}`;
71
+ const changeId = `change:${eventId}`;
72
+ this.eventIds.push(...[clickId, changeId]);
73
+ if (onClick) this.registerSubscription(clickId, onClick);
61
74
  if (onChange) {
62
- this.registerSubscription(id, (v) => onChange(v));
75
+ this.registerSubscription(changeId, (v) => onChange(v));
63
76
  }
64
77
  return {
65
78
  ...rest,
66
79
  key: id,
67
80
  construct,
68
81
  action: {
69
- click: onClick ? this.getPluginChannel(id) : void 0,
70
- change: onChange ? this.getPluginChannel(id) : void 0
82
+ click: onClick ? this.getPluginChannel(clickId) : void 0,
83
+ change: onChange ? this.getPluginChannel(changeId) : void 0
71
84
  }
72
85
  };
73
86
  }
@@ -75,7 +88,7 @@ var Plugin = class {
75
88
  this.sendMessage({ type: message, arg });
76
89
  }
77
90
  onSidebarLoad() {
78
- this.index = {};
91
+ this.index = -1;
79
92
  this.registerHandler("sidebar-load", () => {
80
93
  return this.renderSidebar();
81
94
  });
@@ -150,33 +163,33 @@ var Plugin = class {
150
163
  on(event, callback) {
151
164
  this.registerSubscription(event, callback);
152
165
  }
153
- registerStatusBar() {
154
- const arg = this.renderStatusBar();
155
- if (!arg) return;
156
- const { onClick, ...rest } = arg;
157
- if (arg) {
158
- this.sendMessage({
159
- type: `status`,
160
- arg: rest
161
- });
162
- this.off("status-click");
163
- this.registerSubscription("status-click", onClick);
164
- }
165
- }
166
- onStatusLoad() {
167
- this.registerHandler("status-load", () => {
168
- return this.renderStatusBar();
169
- });
170
- }
166
+ // -----------------------------
167
+ // Status Bar
168
+ // -----------------------------
169
+ // abstract renderStatusBar(): RenderStatusType | null;
170
+ // private registerStatusBar(): void {
171
+ // const arg = this.renderStatusBar();
172
+ // if (!arg) return;
173
+ // const { onClick, ...rest } = arg;
174
+ // if (arg) {
175
+ // this.sendMessage({
176
+ // type: `status`,
177
+ // arg: rest,
178
+ // });
179
+ // this.off("status-click");
180
+ // this.registerSubscription("status-click", onClick);
181
+ // }
182
+ // }
183
+ // private onStatusLoad(): void {
184
+ // this.registerHandler("status-load", () => {
185
+ // return this.renderStatusBar();
186
+ // });
187
+ // }
171
188
  // -----------------------------
172
189
  // State
173
190
  // -----------------------------
174
191
  setState(param) {
175
- this.index = {};
176
192
  this.store.set(param);
177
- const sidebar = this.renderSidebar();
178
- if (sidebar) this.render("sidebar", sidebar);
179
- this.registerStatusBar();
180
193
  }
181
194
  getState() {
182
195
  return this.store.get();
@@ -184,10 +197,21 @@ var Plugin = class {
184
197
  // -----------------------------
185
198
  // Logging
186
199
  // -----------------------------
187
- notify(message) {
200
+ // protected notify(message: string): void {
201
+ // this.sendMessage({
202
+ // type: "notify",
203
+ // arg: message,
204
+ // });
205
+ // }
206
+ // -----------------------------
207
+ // Utils
208
+ // -----------------------------
209
+ selectFile(filePath) {
188
210
  this.sendMessage({
189
- type: "notify",
190
- arg: message
211
+ type: "select-file",
212
+ arg: {
213
+ filePath
214
+ }
191
215
  });
192
216
  }
193
217
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bb-relay",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "For managing bb-editor extension",
5
5
  "license": "ISC",
6
6
  "author": "Ade Adeola",
@@ -48,6 +48,7 @@
48
48
  "typescript-eslint": "^8.44.1"
49
49
  },
50
50
  "dependencies": {
51
+ "fast-deep-equal": "^3.1.3",
51
52
  "lucide-react": "^0.563.0"
52
53
  }
53
54
  }
package/src/constant.ts CHANGED
@@ -3,13 +3,5 @@ import WEBSITE from "./constants/WEBSITE";
3
3
  import { DATA_ALIAS } from "./constants/DATA_ALIAS";
4
4
  import PLUGIN_URL from "./constants/PLUGIN_URL";
5
5
  import PROJECT_CATEGORY from "./constants/PROJECT_CATEGORY";
6
- import { COLOR_LIST } from "./constants/COLOR_LIST";
7
6
 
8
- export {
9
- SENTINEL_FILE,
10
- WEBSITE,
11
- DATA_ALIAS,
12
- PLUGIN_URL,
13
- PROJECT_CATEGORY,
14
- COLOR_LIST,
15
- };
7
+ export { SENTINEL_FILE, WEBSITE, DATA_ALIAS, PLUGIN_URL, PROJECT_CATEGORY };
package/src/editor.ts CHANGED
@@ -13,7 +13,6 @@ import { WatchFSEvent } from "./types/editor/WatchFSEvent";
13
13
  import FileStat from "./types/editor/FileStat";
14
14
  import Result from "./types/editor/Result";
15
15
  import { FolderStat } from "./types/editor/FolderStat";
16
- import Color from "./constants/COLOR_LIST";
17
16
 
18
17
  export type {
19
18
  Commit,
@@ -32,5 +31,4 @@ export type {
32
31
  FolderStat,
33
32
  Result,
34
33
  Shortcut,
35
- Color,
36
34
  };
@@ -24,5 +24,6 @@ export type BaseInstruction<T extends InstructionTypes> = {
24
24
  click?: string;
25
25
  change?: string;
26
26
  };
27
- children?: ElementInstruction | ElementInstruction[] | string;
27
+ children?: ElementInstruction | (ElementInstruction | null)[] | string | null;
28
+ scrollIntoView?: boolean
28
29
  };
@@ -1,6 +1,3 @@
1
- import { ElementInstruction } from ".";
2
1
  import { BaseInstruction } from "./BaseInstruction";
3
2
 
4
- export interface ViewInstruction extends BaseInstruction<"view"> {
5
- children: ElementInstruction[];
6
- }
3
+ export interface ViewInstruction extends BaseInstruction<"view"> {}
@@ -1,6 +1,6 @@
1
1
  import { ElementInstruction } from "./ElementInstruction";
2
2
  import { PluginElement } from "./PluginElement";
3
- import { RenderStatusType } from "./RenderStatusType";
3
+ // import { RenderStatusType } from "./RenderStatusType";
4
4
  import { EventHandler } from "./EventHandler";
5
5
  import { getPluginChannel } from "./getPluginChannel";
6
6
  import {
@@ -11,11 +11,12 @@ import {
11
11
  Result,
12
12
  } from "@/editor";
13
13
  import { Project } from "@/database";
14
+ import equal from "fast-deep-equal";
14
15
 
15
16
  export type Store<T extends Record<string, unknown>> = {
16
17
  get(): T;
17
18
  set(next: SetState<T>): void;
18
- subscribe(listener: (state: T) => void): () => void;
19
+ subscribe(listener: (state: T, prevState: T) => void): () => void;
19
20
  };
20
21
 
21
22
  // TODO: flesh it out properly
@@ -39,7 +40,7 @@ export type EditorAPI = {
39
40
  filePath: string,
40
41
  destination: string,
41
42
  ) => Promise<Result<string>>;
42
- listFiles: (folder: string, glob?: string) => Promise<Result<string[]>>;
43
+ listFiles: (glob?: string) => Promise<Result<string[]>>;
43
44
  };
44
45
  // project: getProjectInfo, workingDirectory
45
46
  // dialog: selectFolder, selectFile
@@ -75,8 +76,9 @@ export abstract class Plugin<T extends Record<string, unknown>> {
75
76
  private subscriptions = new Map<string, () => void>();
76
77
  private subscribe: (channel: string, handler: EventHandler) => () => void;
77
78
  private sendMessage: (arg: { type: string; arg: unknown }) => void;
79
+ private eventIds: string[] = [];
78
80
 
79
- private index: { [key: string]: number } = {};
81
+ private index: number = -1;
80
82
 
81
83
  /** App api, the ones available will be based on the permissions in the manifest*/
82
84
  protected editor: EditorAPI = {};
@@ -112,10 +114,19 @@ export abstract class Plugin<T extends Record<string, unknown>> {
112
114
 
113
115
  this.store = store;
114
116
  const value = store.get();
115
- store.set({ ...this.init(), ...value });
117
+ this.setState({ ...this.init(), ...value });
118
+
119
+ this.store.subscribe((curr, prev) => {
120
+ const isEqual = equal(curr, prev);
121
+ if (isEqual) return;
122
+ this.index = -1;
123
+ this.eventIds.forEach((event) => this.off(event));
124
+ this.eventIds = [];
125
+ const sidebar = this.renderSidebar();
126
+ if (sidebar) this.render("sidebar", sidebar);
127
+ });
116
128
 
117
129
  this.onSidebarLoad();
118
- this.onStatusLoad();
119
130
  }
120
131
 
121
132
  // -----------------------------
@@ -124,13 +135,16 @@ export abstract class Plugin<T extends Record<string, unknown>> {
124
135
 
125
136
  private makeElement(construct: ElementInstruction["construct"], props: any) {
126
137
  const { onClick, onChange, key, ...rest } = props;
127
- const elementIndex = this.index[construct] || 0;
128
- const id = key ?? construct + elementIndex;
129
- this.index[construct] = elementIndex + 1;
130
-
131
- if (onClick) this.registerSubscription(id, onClick);
138
+ const id = key ?? construct + this.index;
139
+ this.index += 1;
140
+
141
+ const eventId = crypto.randomUUID();
142
+ const clickId = `click:${eventId}`;
143
+ const changeId = `change:${eventId}`;
144
+ this.eventIds.push(...[clickId, changeId]);
145
+ if (onClick) this.registerSubscription(clickId, onClick);
132
146
  if (onChange) {
133
- this.registerSubscription(id, (v) => onChange(v));
147
+ this.registerSubscription(changeId, (v) => onChange(v));
134
148
  }
135
149
 
136
150
  return {
@@ -138,8 +152,8 @@ export abstract class Plugin<T extends Record<string, unknown>> {
138
152
  key: id,
139
153
  construct,
140
154
  action: {
141
- click: onClick ? this.getPluginChannel(id) : undefined,
142
- change: onChange ? this.getPluginChannel(id) : undefined,
155
+ click: onClick ? this.getPluginChannel(clickId) : undefined,
156
+ change: onChange ? this.getPluginChannel(changeId) : undefined,
143
157
  },
144
158
  };
145
159
  }
@@ -201,7 +215,7 @@ export abstract class Plugin<T extends Record<string, unknown>> {
201
215
  abstract renderSidebar(): ElementInstruction | null;
202
216
 
203
217
  private onSidebarLoad(): void {
204
- this.index = {};
218
+ this.index = -1;
205
219
  this.registerHandler("sidebar-load", () => {
206
220
  return this.renderSidebar();
207
221
  });
@@ -325,40 +339,34 @@ export abstract class Plugin<T extends Record<string, unknown>> {
325
339
  // Status Bar
326
340
  // -----------------------------
327
341
 
328
- abstract renderStatusBar(): RenderStatusType | null;
329
-
330
- private registerStatusBar(): void {
331
- const arg = this.renderStatusBar();
332
- if (!arg) return;
333
- const { onClick, ...rest } = arg;
334
- if (arg) {
335
- this.sendMessage({
336
- type: `status`,
337
- arg: rest,
338
- });
339
- this.off("status-click");
340
- this.registerSubscription("status-click", onClick);
341
- }
342
- }
343
-
344
- private onStatusLoad(): void {
345
- this.registerHandler("status-load", () => {
346
- return this.renderStatusBar();
347
- });
348
- }
342
+ // abstract renderStatusBar(): RenderStatusType | null;
343
+
344
+ // private registerStatusBar(): void {
345
+ // const arg = this.renderStatusBar();
346
+ // if (!arg) return;
347
+ // const { onClick, ...rest } = arg;
348
+ // if (arg) {
349
+ // this.sendMessage({
350
+ // type: `status`,
351
+ // arg: rest,
352
+ // });
353
+ // this.off("status-click");
354
+ // this.registerSubscription("status-click", onClick);
355
+ // }
356
+ // }
357
+
358
+ // private onStatusLoad(): void {
359
+ // this.registerHandler("status-load", () => {
360
+ // return this.renderStatusBar();
361
+ // });
362
+ // }
349
363
 
350
364
  // -----------------------------
351
365
  // State
352
366
  // -----------------------------
353
367
 
354
368
  protected setState(param: SetState<T>): void {
355
- this.index = {};
356
369
  this.store.set(param);
357
-
358
- const sidebar = this.renderSidebar();
359
- if (sidebar) this.render("sidebar", sidebar);
360
-
361
- this.registerStatusBar();
362
370
  }
363
371
 
364
372
  protected getState(): T {
@@ -369,10 +377,22 @@ export abstract class Plugin<T extends Record<string, unknown>> {
369
377
  // Logging
370
378
  // -----------------------------
371
379
 
372
- protected notify(message: string): void {
380
+ // protected notify(message: string): void {
381
+ // this.sendMessage({
382
+ // type: "notify",
383
+ // arg: message,
384
+ // });
385
+ // }
386
+
387
+ // -----------------------------
388
+ // Utils
389
+ // -----------------------------
390
+ selectFile(filePath: string) {
373
391
  this.sendMessage({
374
- type: "notify",
375
- arg: message,
392
+ type: "select-file",
393
+ arg: {
394
+ filePath,
395
+ },
376
396
  });
377
397
  }
378
398
  }
@@ -65,6 +65,11 @@ const validateManifest = (manifestContent: string) => {
65
65
  } else fileChecks.push(content.entry);
66
66
  }
67
67
 
68
+ if (content.theme) {
69
+ if (!content.theme.name) errors.push("No name for theme");
70
+ if (!content.theme.path) errors.push("No css file path provided");
71
+ else fileChecks.push(content.theme.path);
72
+ }
68
73
  return {
69
74
  errors,
70
75
  fileChecks,
@@ -31,5 +31,9 @@ export type Manifest = {
31
31
  author?: string;
32
32
  homepage?: string;
33
33
  repository?: string;
34
+ theme?: {
35
+ name: string;
36
+ path: string;
37
+ };
34
38
  [key: string]: unknown;
35
39
  };
@@ -1,4 +1,3 @@
1
- import Color from "@/constants/COLOR_LIST";
2
1
  import Language from "./Language";
3
2
  import Locale from "./Locale";
4
3
 
@@ -28,7 +27,6 @@ export type Shortcut = {
28
27
  };
29
28
 
30
29
  export type Settings = {
31
- theme: "light" | "dark" | "system";
32
30
  language?: Locale;
33
31
  sidebarWidth: number;
34
32
  baseAppFont: number;
@@ -37,7 +35,7 @@ export type Settings = {
37
35
  showAllFile: boolean;
38
36
  trashBehaviour: "permanent" | "trash";
39
37
  shortcuts: Shortcut[];
40
- color: Color;
38
+ theme: string;
41
39
  /**
42
40
  * { [fileExtension: string]: pluginId }
43
41
  */
@@ -1,4 +0,0 @@
1
- declare const COLOR_LIST: readonly ["red", "slate", "stone", "zinc", "gray", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose"];
2
- type Color = (typeof COLOR_LIST)[number];
3
-
4
- export { COLOR_LIST as C, type Color as a };
@@ -1,4 +0,0 @@
1
- declare const COLOR_LIST: readonly ["red", "slate", "stone", "zinc", "gray", "orange", "amber", "yellow", "lime", "green", "emerald", "teal", "cyan", "sky", "blue", "indigo", "violet", "purple", "fuchsia", "pink", "rose"];
2
- type Color = (typeof COLOR_LIST)[number];
3
-
4
- export { COLOR_LIST as C, type Color as a };
@@ -1,27 +0,0 @@
1
- export const COLOR_LIST = [
2
- "red",
3
- "slate",
4
- "stone",
5
- "zinc",
6
- "gray",
7
- "orange",
8
- "amber",
9
- "yellow",
10
- "lime",
11
- "green",
12
- "emerald",
13
- "teal",
14
- "cyan",
15
- "sky",
16
- "blue",
17
- "indigo",
18
- "violet",
19
- "purple",
20
- "fuchsia",
21
- "pink",
22
- "rose",
23
- ] as const;
24
-
25
- type Color = (typeof COLOR_LIST)[number];
26
-
27
- export default Color;