alinea 0.5.4 → 0.5.6

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 (50) hide show
  1. package/dist/backend/Database.js +3 -2
  2. package/dist/backend/Store.js +1 -1
  3. package/dist/backend/resolver/EntryResolver.js +5 -2
  4. package/dist/chunks/{chunk-TY7XOXM3.js → chunk-CYNFUAZ7.js} +3 -1
  5. package/dist/chunks/{chunk-TBAB475U.js → chunk-HWXESJ6U.js} +1 -1
  6. package/dist/chunks/{chunk-VRXHB6DJ.js → chunk-LMUDRU2U.js} +2 -1
  7. package/dist/chunks/{sql.js-75HGQX6U.js → sql.js-RSY2XONK.js} +1 -1
  8. package/dist/cli/Generate.js +1 -1
  9. package/dist/cli/Serve.js +2 -2
  10. package/dist/cli/bin.js +1 -1
  11. package/dist/cli/generate/GenerateDashboard.js +1 -1
  12. package/dist/cloud/server/CloudAuthServer.js +1 -1
  13. package/dist/core/EntrySearch.d.ts +1 -0
  14. package/dist/core/EntrySearch.js +1 -0
  15. package/dist/core/Shape.d.ts +1 -1
  16. package/dist/core/Tracker.d.ts +2 -2
  17. package/dist/core/Tracker.js +2 -0
  18. package/dist/core/Type.d.ts +1 -0
  19. package/dist/core/Type.js +4 -0
  20. package/dist/core/Type.test.d.ts +1 -0
  21. package/dist/core/driver/TestDriver.js +1 -1
  22. package/dist/core/field/RichTextField.d.ts +3 -1
  23. package/dist/core/field/RichTextField.js +2 -1
  24. package/dist/core/field/ScalarField.d.ts +3 -1
  25. package/dist/core/field/ScalarField.js +5 -1
  26. package/dist/core/shape/ListShape.d.ts +1 -0
  27. package/dist/core/shape/ListShape.js +13 -0
  28. package/dist/core/shape/RecordShape.d.ts +1 -0
  29. package/dist/core/shape/RecordShape.js +8 -0
  30. package/dist/core/shape/RichTextShape.d.ts +5 -2
  31. package/dist/core/shape/RichTextShape.js +26 -1
  32. package/dist/core/shape/ScalarShape.d.ts +3 -1
  33. package/dist/core/shape/ScalarShape.js +8 -1
  34. package/dist/core/shape/UnionShape.d.ts +1 -0
  35. package/dist/core/shape/UnionShape.js +16 -0
  36. package/dist/core/util/EntryRows.js +4 -1
  37. package/dist/dashboard/atoms/FormAtoms.d.ts +5 -5
  38. package/dist/dashboard/atoms/FormAtoms.js +7 -7
  39. package/dist/dashboard/editor/UseField.js +3 -3
  40. package/dist/dashboard/hook/UseExplorer.d.ts +1 -0
  41. package/dist/dashboard/util/PersistentStore.js +1 -1
  42. package/dist/dashboard/view/entry/EntrySummary.js +2 -2
  43. package/dist/dashboard/view/entry/NewEntry.js +63 -26
  44. package/dist/dashboard/view/explorer/Explorer.d.ts +1 -0
  45. package/dist/dashboard/view/explorer/ExplorerItem.js +1 -1
  46. package/dist/index.css +3 -2
  47. package/dist/input/richtext/RichTextField.d.ts +2 -6
  48. package/dist/input/text/TextField.d.ts +2 -0
  49. package/dist/picker/entry/EntryPicker.browser.js +2 -1
  50. package/package.json +1 -1
@@ -5,7 +5,7 @@ import {
5
5
  alias,
6
6
  create,
7
7
  exists
8
- } from "../chunks/chunk-TY7XOXM3.js";
8
+ } from "../chunks/chunk-CYNFUAZ7.js";
9
9
  import {
10
10
  Expr
11
11
  } from "../chunks/chunk-4JLFL6LD.js";
@@ -17,6 +17,7 @@ import {
17
17
  PageSeed,
18
18
  Root,
19
19
  Schema,
20
+ Type,
20
21
  Workspace,
21
22
  createId,
22
23
  unreachable
@@ -438,7 +439,7 @@ ${JSON.stringify(mutation)}`
438
439
  ...data,
439
440
  path: pathData
440
441
  };
441
- const searchableText = "";
442
+ const searchableText = Type.searchableText(type, entryData);
442
443
  return {
443
444
  workspace: meta.workspace,
444
445
  root: meta.root,
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  connect
3
- } from "../chunks/chunk-VRXHB6DJ.js";
3
+ } from "../chunks/chunk-LMUDRU2U.js";
4
4
  import "../chunks/chunk-FLZ4KUMA.js";
5
5
  import "../chunks/chunk-4JLFL6LD.js";
6
6
  import "../chunks/chunk-U5RRZUYZ.js";
@@ -1,9 +1,10 @@
1
1
  import {
2
+ bm25,
2
3
  count,
3
4
  iif,
4
5
  match,
5
6
  withRecursive
6
- } from "../../chunks/chunk-TY7XOXM3.js";
7
+ } from "../../chunks/chunk-CYNFUAZ7.js";
7
8
  import {
8
9
  BinOpType,
9
10
  Expr,
@@ -365,7 +366,9 @@ var EntryResolver = class {
365
366
  extra.singleResult = true;
366
367
  if (groupBy)
367
368
  extra.groupBy = groupBy.map((expr) => this.expr(ctx, expr));
368
- if (orderBy)
369
+ if (searchTerms)
370
+ extra.orderBy = [bm25(EntrySearch, 20, 1).asc()];
371
+ else if (orderBy)
369
372
  extra.orderBy = this.orderBy(ctx, orderBy);
370
373
  return query[Query.Data].with(extra);
371
374
  }
@@ -36,6 +36,7 @@ var {
36
36
  iif,
37
37
  exists,
38
38
  match,
39
+ bm25,
39
40
  highlight,
40
41
  snippet,
41
42
  cast,
@@ -112,5 +113,6 @@ export {
112
113
  count,
113
114
  iif,
114
115
  exists,
115
- match
116
+ match,
117
+ bm25
116
118
  };
@@ -2,7 +2,7 @@
2
2
  var package_default = {
3
3
  bin: "./dist/cli.js",
4
4
  name: "alinea",
5
- version: "0.5.4",
5
+ version: "0.5.6",
6
6
  license: "MIT",
7
7
  type: "module",
8
8
  scripts: {
@@ -335,8 +335,8 @@ var AsyncDriver = class extends DriverBase {
335
335
  }
336
336
  }
337
337
  async transaction(run) {
338
- const id = `t${this.transactionId++}`;
339
338
  const [connection, release] = await this.isolate();
339
+ const id = `t${this.transactionId++}`;
340
340
  await connection.executeQuery(
341
341
  new QueryData.Transaction({ op: QueryData.TransactionOperation.Begin, id })
342
342
  );
@@ -1353,6 +1353,7 @@ var SqliteFormatter = class extends Formatter {
1353
1353
  stmt.raw(" MATCH ");
1354
1354
  this.formatExprValue(ctx, query);
1355
1355
  return stmt;
1356
+ case "bm25":
1356
1357
  case "highlight":
1357
1358
  case "snippet":
1358
1359
  stmt.identifier(expr.method);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  SqlJsDriver,
3
3
  connect
4
- } from "./chunk-VRXHB6DJ.js";
4
+ } from "./chunk-LMUDRU2U.js";
5
5
  import "./chunk-FLZ4KUMA.js";
6
6
  import "./chunk-4JLFL6LD.js";
7
7
  import "./chunk-U5RRZUYZ.js";
@@ -27,7 +27,7 @@ function generatePackage(context, config) {
27
27
  async function createDb() {
28
28
  const { default: sqlite } = await import("@alinea/sqlite-wasm");
29
29
  const { Database } = await sqlite();
30
- const { connect } = await import("../chunks/sql.js-75HGQX6U.js");
30
+ const { connect } = await import("../chunks/sql.js-RSY2XONK.js");
31
31
  const db = new Database();
32
32
  const store = connect(db).toAsync();
33
33
  return [store, () => db.export()];
package/dist/cli/Serve.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../chunks/chunk-TBAB475U.js";
3
+ } from "../chunks/chunk-HWXESJ6U.js";
4
4
  import "../chunks/chunk-U5RRZUYZ.js";
5
5
 
6
6
  // src/cli/Serve.ts
@@ -49,7 +49,7 @@ async function serve(options) {
49
49
  liveReload: new LiveReload()
50
50
  };
51
51
  server.then(async () => {
52
- console.log(` \x1B[36mAlinea ${package_default.version}\x1B[39m`);
52
+ console.log(` \x1B[36m\u03B1 Alinea ${package_default.version}\x1B[39m`);
53
53
  console.log(` - Local CMS: ${await dashboardUrl}
54
54
  `);
55
55
  });
package/dist/cli/bin.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../chunks/chunk-TBAB475U.js";
3
+ } from "../chunks/chunk-HWXESJ6U.js";
4
4
  import "../chunks/chunk-U5RRZUYZ.js";
5
5
 
6
6
  // node_modules/mri/lib/index.mjs
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  package_default
3
- } from "../../chunks/chunk-TBAB475U.js";
3
+ } from "../../chunks/chunk-HWXESJ6U.js";
4
4
  import {
5
5
  __commonJS,
6
6
  __toESM
@@ -3,7 +3,7 @@ import {
3
3
  } from "../../chunks/chunk-IKINPSS5.js";
4
4
  import {
5
5
  package_default
6
- } from "../../chunks/chunk-TBAB475U.js";
6
+ } from "../../chunks/chunk-HWXESJ6U.js";
7
7
  import "../../chunks/chunk-U5RRZUYZ.js";
8
8
 
9
9
  // src/cloud/server/CloudAuthServer.ts
@@ -2,5 +2,6 @@ import { table } from 'rado';
2
2
  export declare const EntrySearch: import("rado").Table<{
3
3
  title: import("rado").ValueColumn<string>;
4
4
  searchableText: import("rado").ValueColumn<string>;
5
+ rank: import("rado").ValueColumn<number>;
5
6
  }>;
6
7
  export type EntrySearch = table<typeof EntrySearch>;
@@ -9,6 +9,7 @@ var EntrySearch = table({
9
9
  EntrySearch: class {
10
10
  title = column.string;
11
11
  searchableText = column.string;
12
+ rank = column.number;
12
13
  }
13
14
  });
14
15
  export {
@@ -12,7 +12,7 @@ export interface Shape<Value = any, Mutator = any> {
12
12
  init(parent: YType, key: string): void;
13
13
  watch(parent: YType, key: string): (fun: () => void) => () => void;
14
14
  mutator(parent: YType, key: string, readOnly: boolean): Mutator;
15
- toString(): string;
16
15
  applyLinks(value: Value, loader: LinkResolver): Promise<void>;
16
+ searchableText(value: Value): string;
17
17
  }
18
18
  export {};
@@ -15,6 +15,6 @@ export interface MutationTracker {
15
15
  export declare function optionTrackerOf(field: Field): OptionsTracker<any> | undefined;
16
16
  export declare function valueTrackerOf(field: Field): ValueTracker<any> | undefined;
17
17
  export declare namespace track {
18
- function options<Value, OnChange, Options extends FieldOptions<Value>>(field: Field<Value, OnChange, Options>, tracker: OptionsTracker<Options>): void;
19
- function value<Value>(field: Field<Value>, tracker: ValueTracker<Value>): void;
18
+ function options<Value, OnChange, Options extends FieldOptions<Value>>(field: Field<Value, OnChange, Options>, tracker: OptionsTracker<Options>): Field<Value, OnChange, Options>;
19
+ function value<Value>(field: Field<Value>, tracker: ValueTracker<Value>): Field<Value>;
20
20
  }
@@ -14,10 +14,12 @@ var track;
14
14
  ((track2) => {
15
15
  function options(field, tracker) {
16
16
  optionTrackers.set(Field.ref(field), tracker);
17
+ return field;
17
18
  }
18
19
  track2.options = options;
19
20
  function value(field, tracker) {
20
21
  valueTrackers.set(Field.ref(field), tracker);
22
+ return field;
21
23
  }
22
24
  track2.value = value;
23
25
  })(track || (track = {}));
@@ -64,6 +64,7 @@ export declare namespace Type {
64
64
  function label(type: Type): Label;
65
65
  function meta(type: Type): TypeMeta;
66
66
  function shape(type: Type): RecordShape;
67
+ function searchableText(type: Type, value: any): string;
67
68
  function fields(type: Type): Record<string, Field>;
68
69
  function hint(type: Type): Hint;
69
70
  function sections(type: Type): Section[];
package/dist/core/Type.js CHANGED
@@ -34,6 +34,10 @@ var Type;
34
34
  return type2[Type2.Data].shape;
35
35
  }
36
36
  Type2.shape = shape;
37
+ function searchableText(type2, value) {
38
+ return shape(type2).searchableText(value).trim();
39
+ }
40
+ Type2.searchableText = searchableText;
37
41
  function fields(type2) {
38
42
  return type2;
39
43
  }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  connect
3
- } from "../../chunks/chunk-VRXHB6DJ.js";
3
+ } from "../../chunks/chunk-LMUDRU2U.js";
4
4
  import "../../chunks/chunk-FLZ4KUMA.js";
5
5
  import "../../chunks/chunk-4JLFL6LD.js";
6
6
  import "../../chunks/chunk-U5RRZUYZ.js";
@@ -2,7 +2,9 @@ import { RichTextMutator } from 'alinea/core';
2
2
  import { Field, FieldMeta, FieldOptions } from '../Field.js';
3
3
  import { TextDoc } from '../TextDoc.js';
4
4
  import { RecordShape } from '../shape/RecordShape.js';
5
- export declare class RichTextField<Blocks, Options extends FieldOptions<TextDoc<Blocks>>> extends Field<TextDoc<Blocks>, RichTextMutator<Blocks>, Options> {
5
+ export declare class RichTextField<Blocks, Options extends FieldOptions<TextDoc<Blocks>> & {
6
+ searchable?: boolean;
7
+ }> extends Field<TextDoc<Blocks>, RichTextMutator<Blocks>, Options> {
6
8
  constructor(shape: {
7
9
  [key: string]: RecordShape<any>;
8
10
  } | undefined, meta: FieldMeta<TextDoc<Blocks>, RichTextMutator<Blocks>, Options>);
@@ -9,7 +9,8 @@ var RichTextField = class extends Field {
9
9
  shape: new RichTextShape(
10
10
  meta.options.label,
11
11
  shape,
12
- meta.options.initialValue
12
+ meta.options.initialValue,
13
+ meta.options.searchable
13
14
  ),
14
15
  ...meta
15
16
  });
@@ -1,4 +1,6 @@
1
1
  import { Field, FieldMeta, FieldOptions } from '../Field.js';
2
- export declare class ScalarField<Value, Options extends FieldOptions<Value>> extends Field<Value, (value: Value) => void, Options> {
2
+ export declare class ScalarField<Value, Options extends FieldOptions<Value> & {
3
+ searchable?: boolean;
4
+ }> extends Field<Value, (value: Value) => void, Options> {
3
5
  constructor(meta: FieldMeta<Value, (value: Value) => void, Options>);
4
6
  }
@@ -6,7 +6,11 @@ import { ScalarShape } from "../shape/ScalarShape.js";
6
6
  var ScalarField = class extends Field {
7
7
  constructor(meta) {
8
8
  super({
9
- shape: new ScalarShape(meta.options.label, meta.options.initialValue),
9
+ shape: new ScalarShape(
10
+ meta.options.label,
11
+ meta.options.initialValue,
12
+ meta.options.searchable
13
+ ),
10
14
  ...meta
11
15
  });
12
16
  }
@@ -36,4 +36,5 @@ export declare class ListShape<T extends ListRow> implements Shape<Array<T>, Lis
36
36
  move: (oldIndex: number, newIndex: number) => void;
37
37
  };
38
38
  applyLinks(value: Array<T>, loader: LinkResolver): Promise<void>;
39
+ searchableText(value: Array<T>): string;
39
40
  }
@@ -206,6 +206,19 @@ var ListShape = class {
206
206
  if (this.postProcess)
207
207
  await this.postProcess(value, loader);
208
208
  }
209
+ searchableText(value) {
210
+ let res = "";
211
+ const rows = Array.isArray(value) ? value : [];
212
+ for (const row of rows) {
213
+ const id = row.id;
214
+ const type = row.type;
215
+ const shape = this.values[type];
216
+ if (!id || !type || !shape)
217
+ continue;
218
+ res += shape.searchableText(row);
219
+ }
220
+ return res;
221
+ }
209
222
  };
210
223
  export {
211
224
  ListShape
@@ -21,4 +21,5 @@ export declare class RecordShape<T = object> implements Shape<T, RecordMutator<T
21
21
  set: <K extends keyof T>(k: K, v: T[K]) => void;
22
22
  };
23
23
  applyLinks(value: T, loader: LinkResolver): Promise<void>;
24
+ searchableText(value: T): string;
24
25
  }
@@ -81,6 +81,14 @@ var RecordShape = class _RecordShape {
81
81
  }
82
82
  await Promise.all(tasks);
83
83
  }
84
+ searchableText(value) {
85
+ let res = "";
86
+ const self = value || {};
87
+ for (const key of keys(this.properties)) {
88
+ res += this.properties[key].searchableText(self[key]);
89
+ }
90
+ return res;
91
+ }
84
92
  };
85
93
  export {
86
94
  RecordShape
@@ -2,7 +2,7 @@ import { LinkResolver } from 'alinea/backend/resolver/LinkResolver';
2
2
  import * as Y from 'yjs';
3
3
  import { Label } from '../Label.js';
4
4
  import { Shape } from '../Shape.js';
5
- import { TextDoc } from '../TextDoc.js';
5
+ import { TextDoc, TextNode } from '../TextDoc.js';
6
6
  import { RecordShape } from './RecordShape.js';
7
7
  export type RichTextMutator<R> = {
8
8
  readOnly: boolean;
@@ -25,8 +25,9 @@ export declare class RichTextShape<Blocks> implements Shape<TextDoc<Blocks>, Ric
25
25
  label: Label;
26
26
  shapes?: Record<string, RecordShape<object>> | undefined;
27
27
  initialValue?: TextDoc<Blocks> | undefined;
28
+ searchable?: boolean | undefined;
28
29
  values: Record<string, RecordShape>;
29
- constructor(label: Label, shapes?: Record<string, RecordShape<object>> | undefined, initialValue?: TextDoc<Blocks> | undefined);
30
+ constructor(label: Label, shapes?: Record<string, RecordShape<object>> | undefined, initialValue?: TextDoc<Blocks> | undefined, searchable?: boolean | undefined);
30
31
  create(): TextDoc<Blocks>;
31
32
  toXml(rows: TextDoc<Blocks>): (Y.XmlElement<{
32
33
  [key: string]: string;
@@ -43,4 +44,6 @@ export declare class RichTextShape<Blocks> implements Shape<TextDoc<Blocks>, Ric
43
44
  insert: (id: string, block: string) => void;
44
45
  };
45
46
  applyLinks(doc: TextDoc<Blocks>, loader: LinkResolver): Promise<void>;
47
+ searchableText(value: TextDoc<Blocks>): string;
48
+ textOf(node: TextNode<any>): string;
46
49
  }
@@ -76,10 +76,11 @@ function unserialize(node) {
76
76
  }
77
77
  var linkInfoFields = void 0;
78
78
  var RichTextShape = class {
79
- constructor(label, shapes, initialValue) {
79
+ constructor(label, shapes, initialValue, searchable) {
80
80
  this.label = label;
81
81
  this.shapes = shapes;
82
82
  this.initialValue = initialValue;
83
+ this.searchable = searchable;
83
84
  this.values = shapes ? fromEntries(
84
85
  entries(shapes).map(([key, value]) => {
85
86
  return [
@@ -298,6 +299,30 @@ var RichTextShape = class {
298
299
  )
299
300
  );
300
301
  }
302
+ searchableText(value) {
303
+ let res = "";
304
+ if (!this.searchable)
305
+ return res;
306
+ if (!Array.isArray(value))
307
+ return res;
308
+ return value.reduce((acc, node) => {
309
+ return acc + this.textOf(node);
310
+ }, "");
311
+ }
312
+ textOf(node) {
313
+ if (this.values[node.type]) {
314
+ const shape = this.values[node.type];
315
+ return shape.searchableText(node);
316
+ }
317
+ if (node.type === "text")
318
+ return " " + node.text;
319
+ if ("content" in node && Array.isArray(node.content)) {
320
+ return node.content.reduce((acc, node2) => {
321
+ return acc + this.textOf(node2);
322
+ }, "");
323
+ }
324
+ return "";
325
+ }
301
326
  };
302
327
  function iterMarks(doc, fn) {
303
328
  for (const row of doc) {
@@ -5,7 +5,8 @@ export type ScalarMutator<T> = (value: T) => void;
5
5
  export declare class ScalarShape<T> implements Shape<T, ScalarMutator<T>> {
6
6
  label: Label;
7
7
  initialValue?: T | undefined;
8
- constructor(label: Label, initialValue?: T | undefined);
8
+ searchable?: boolean | undefined;
9
+ constructor(label: Label, initialValue?: T | undefined, searchable?: boolean | undefined);
9
10
  create(): T;
10
11
  toY(value: T): T;
11
12
  fromY(yValue: any): any;
@@ -14,4 +15,5 @@ export declare class ScalarShape<T> implements Shape<T, ScalarMutator<T>> {
14
15
  watch(parent: Y.Map<any>, key: string): (fun: () => void) => () => void;
15
16
  mutator(parent: Y.Map<any>, key: string, readOnly?: boolean): (value: T) => void;
16
17
  applyLinks(): Promise<void>;
18
+ searchableText(value: T): string;
17
19
  }
@@ -2,9 +2,10 @@ import "../../chunks/chunk-U5RRZUYZ.js";
2
2
 
3
3
  // src/core/shape/ScalarShape.ts
4
4
  var ScalarShape = class {
5
- constructor(label, initialValue) {
5
+ constructor(label, initialValue, searchable) {
6
6
  this.label = label;
7
7
  this.initialValue = initialValue;
8
+ this.searchable = searchable;
8
9
  }
9
10
  create() {
10
11
  return this.initialValue;
@@ -43,6 +44,12 @@ var ScalarShape = class {
43
44
  }
44
45
  async applyLinks() {
45
46
  }
47
+ searchableText(value) {
48
+ if (!this.searchable)
49
+ return "";
50
+ const stringified = String(value ?? "");
51
+ return stringified ? " " + stringified : "";
52
+ }
46
53
  };
47
54
  export {
48
55
  ScalarShape
@@ -26,4 +26,5 @@ export declare class UnionShape<T extends UnionRow> implements Shape<T, UnionMut
26
26
  watch(parent: Y.Map<any>, key: string): (fun: () => void) => () => void;
27
27
  mutator(parent: Y.Map<any>, key: string, readOnly: boolean): UnionMutator<T>;
28
28
  applyLinks(value: T, loader: LinkResolver): Promise<void>;
29
+ searchableText(value: T): string;
29
30
  }
@@ -122,6 +122,22 @@ var UnionShape = class {
122
122
  if (this.postProcess)
123
123
  await this.postProcess(value, loader);
124
124
  }
125
+ searchableText(value) {
126
+ let res = "";
127
+ if (Array.isArray(value))
128
+ value = value[0] ?? {};
129
+ else
130
+ value = value ?? {};
131
+ const type = value.type;
132
+ const shape = this.shapes[type];
133
+ const self = value || {};
134
+ if (!shape)
135
+ return "";
136
+ for (const [key, field] of entries(shape.properties)) {
137
+ res += field.searchableText(self[key]);
138
+ }
139
+ return res;
140
+ }
125
141
  };
126
142
  export {
127
143
  UnionShape
@@ -8,12 +8,15 @@ import { entryFilepath, entryInfo, entryUrl } from "../EntryFilenames.js";
8
8
  import { createRecord } from "../EntryRecord.js";
9
9
  import { EntryPhase } from "../EntryRow.js";
10
10
  import { Root } from "../Root.js";
11
+ import { Type } from "../Type.js";
11
12
  async function createEntryRow(config, input) {
12
13
  const record = createRecord(input);
13
14
  const fileContents = JsonLoader.format(config.schema, record);
14
15
  const fileHash = await createFileHash(fileContents);
15
16
  const rowHash = await createRowHash({ ...input, fileHash });
16
- return { ...input, fileHash, rowHash };
17
+ const type = config.schema[input.type];
18
+ const searchableText = Type.searchableText(type, input.data);
19
+ return { ...input, searchableText, fileHash, rowHash };
17
20
  }
18
21
  function entryParentPaths(config, entry) {
19
22
  const root = Root.data(config.workspaces[entry.workspace][entry.root]);
@@ -1,5 +1,5 @@
1
- import { Field, FieldGetter, FieldOptions, Type } from 'alinea/core';
2
- import { Atom, Getter } from 'jotai';
1
+ import { Field, FieldOptions, Type } from 'alinea/core';
2
+ import { Atom } from 'jotai';
3
3
  import { PropsWithChildren } from 'react';
4
4
  import * as Y from 'yjs';
5
5
  export interface FieldInfo<Value = any, Mutator = any, Options extends FieldOptions<Value> = FieldOptions<Value>> {
@@ -13,16 +13,16 @@ export declare class FormAtoms<T = any> {
13
13
  type: Type<T>;
14
14
  container: Y.Map<any>;
15
15
  private options;
16
- fieldInfo: Map<symbol, FieldInfo<any, any, FieldOptions<any>>>;
16
+ private fields;
17
17
  constructor(type: Type<T>, container: Y.Map<any>, options?: {
18
18
  readOnly: boolean;
19
19
  });
20
20
  data(): Type.Infer<T>;
21
- getter: (get: Getter) => FieldGetter;
21
+ private getter;
22
22
  private valueAtom;
23
23
  fieldByKey(key: string): Field;
24
24
  keyOf(field: Field): string;
25
- atomsOf<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): FieldInfo<Value, Mutator, Options>;
25
+ fieldInfo<Value, Mutator, Options extends FieldOptions<Value>>(field: Field<Value, Mutator, Options>): FieldInfo<Value, Mutator, Options>;
26
26
  }
27
27
  export interface UseFormOptions<T> {
28
28
  doc?: Y.Doc;
@@ -51,7 +51,7 @@ var FormAtoms = class {
51
51
  ) : atom(defaultOptions);
52
52
  const valueTracker = valueTrackerOf(field);
53
53
  const value = this.valueAtom(field, key, valueTracker);
54
- this.fieldInfo.set(ref, {
54
+ this.fields.set(ref, {
55
55
  key,
56
56
  field,
57
57
  value,
@@ -62,12 +62,12 @@ var FormAtoms = class {
62
62
  }
63
63
  }, "self");
64
64
  }
65
- fieldInfo = /* @__PURE__ */ new Map();
65
+ fields = /* @__PURE__ */ new Map();
66
66
  data() {
67
67
  return Type.shape(this.type).fromY(this.container);
68
68
  }
69
69
  getter = (get) => (field) => {
70
- const info = this.fieldInfo.get(Field.ref(field));
70
+ const info = this.fields.get(Field.ref(field));
71
71
  if (!info)
72
72
  throw new Error(`Field not found: ${Field.label(field)}`);
73
73
  return get(info.value);
@@ -91,20 +91,20 @@ var FormAtoms = class {
91
91
  });
92
92
  }
93
93
  fieldByKey(key) {
94
- for (const info of this.fieldInfo.values())
94
+ for (const info of this.fields.values())
95
95
  if (info.key === key)
96
96
  return info.field;
97
97
  throw new Error(`Field not found: "${key}"`);
98
98
  }
99
99
  keyOf(field) {
100
- const res = this.fieldInfo.get(Field.ref(field));
100
+ const res = this.fields.get(Field.ref(field));
101
101
  const label = Field.label(field);
102
102
  if (!res)
103
103
  throw new Error(`Field not found: ${label}`);
104
104
  return res.key;
105
105
  }
106
- atomsOf(field) {
107
- const res = this.fieldInfo.get(Field.ref(field));
106
+ fieldInfo(field) {
107
+ const res = this.fields.get(Field.ref(field));
108
108
  const label = Field.label(field);
109
109
  if (!res)
110
110
  throw new Error(`Field not found: ${label}`);
@@ -28,17 +28,17 @@ function useFieldKey(field) {
28
28
  }
29
29
  function useFieldOptions(field) {
30
30
  const atoms = useFormContext();
31
- const atom = atoms.atomsOf(field);
31
+ const atom = atoms.fieldInfo(field);
32
32
  return useAtomValue(atom.options);
33
33
  }
34
34
  function useFieldValue(field) {
35
35
  const atoms = useFormContext();
36
- const atom = atoms.atomsOf(field);
36
+ const atom = atoms.fieldInfo(field);
37
37
  return useAtomValue(atom.value);
38
38
  }
39
39
  function useFieldMutator(field) {
40
40
  const atoms = useFormContext();
41
- const atom = atoms.atomsOf(field);
41
+ const atom = atoms.fieldInfo(field);
42
42
  return atom.mutator;
43
43
  }
44
44
  export {
@@ -7,6 +7,7 @@ type ExplorerContext = {
7
7
  onSelect: (entry: ExporerItemSelect) => void;
8
8
  onNavigate?: (entryId: string) => void;
9
9
  showMedia?: boolean;
10
+ withNavigation?: boolean;
10
11
  border?: boolean;
11
12
  };
12
13
  export declare function useExplorer(): ExplorerContext;
@@ -3,7 +3,7 @@ import {
3
3
  } from "../../chunks/chunk-7YXWNKGS.js";
4
4
  import {
5
5
  connect
6
- } from "../../chunks/chunk-VRXHB6DJ.js";
6
+ } from "../../chunks/chunk-LMUDRU2U.js";
7
7
  import "../../chunks/chunk-FLZ4KUMA.js";
8
8
  import {
9
9
  create,
@@ -2,7 +2,7 @@ import "../../../chunks/chunk-U5RRZUYZ.js";
2
2
 
3
3
  // src/dashboard/view/entry/EntrySummary.tsx
4
4
  import { Entry, Type, view } from "alinea/core";
5
- import { Chip, HStack, TextLabel, Typo, VStack, fromModule } from "alinea/ui";
5
+ import { Chip, HStack, TextLabel, Typo, VStack, fromModule, px } from "alinea/ui";
6
6
  import { Ellipsis } from "alinea/ui/Ellipsis";
7
7
  import { IcRoundKeyboardArrowRight } from "alinea/ui/icons/IcRoundKeyboardArrowRight";
8
8
  import { Fragment } from "react";
@@ -59,7 +59,7 @@ var EntrySummaryRow = view(
59
59
  return null;
60
60
  return /* @__PURE__ */ jsxs(HStack, { center: true, full: true, gap: 10, className: styles.row(), children: [
61
61
  /* @__PURE__ */ jsxs(VStack, { children: [
62
- parents.length > 0 && /* @__PURE__ */ jsx(Ellipsis, { children: /* @__PURE__ */ jsx(Typo.Small, { children: /* @__PURE__ */ jsx(HStack, { center: true, gap: 3, children: parents.map(({ entryId: entryId2, title: title2 }) => /* @__PURE__ */ jsx(Fragment, { children: title2 }, entryId2)).reduce((prev, curr, i) => [
62
+ parents.length > 0 && /* @__PURE__ */ jsx(Ellipsis, { style: { marginTop: px(-1) }, children: /* @__PURE__ */ jsx(Typo.Small, { children: /* @__PURE__ */ jsx(HStack, { center: true, gap: 3, children: parents.map(({ entryId: entryId2, title: title2 }) => /* @__PURE__ */ jsx(Fragment, { children: title2 }, entryId2)).reduce((prev, curr, i) => [
63
63
  prev,
64
64
  /* @__PURE__ */ jsx(IcRoundKeyboardArrowRight, {}, `s${i}`),
65
65
  curr
@@ -36,9 +36,10 @@ import { Modal } from "alinea/dashboard/view/Modal";
36
36
  import { link } from "alinea/input/link";
37
37
  import { select } from "alinea/input/select";
38
38
  import { text } from "alinea/input/text";
39
+ import { entryFields, entryPicker } from "alinea/picker/entry/EntryPicker";
39
40
  import { Button, Loader, fromModule } from "alinea/ui";
40
41
  import { Link } from "alinea/ui/Link";
41
- import { Suspense, useMemo, useState } from "react";
42
+ import { Suspense, useEffect, useMemo, useState } from "react";
42
43
  import { changedEntriesAtom, graphAtom, useMutate } from "../../atoms/DbAtoms.js";
43
44
  import { useConfig } from "../../hook/UseConfig.js";
44
45
  import { useLocale } from "../../hook/UseLocale.js";
@@ -102,8 +103,8 @@ function NewEntryForm({ parentId }) {
102
103
  return Type.meta(type2).isContainer;
103
104
  }).map((pair) => pair[0]);
104
105
  const root = useRoot();
105
- const parentField = useMemo(
106
- () => link.entry("Parent", {
106
+ const parentField = useMemo(() => {
107
+ return link.entry("Parent", {
107
108
  condition: Entry.type.isIn(containerTypes).and(Entry.workspace.is(workspace)).and(Entry.root.is(root.name)),
108
109
  initialValue: preselectedId ? {
109
110
  id: "parent",
@@ -111,26 +112,25 @@ function NewEntryForm({ parentId }) {
111
112
  type: "entry",
112
113
  entry: preselectedId
113
114
  } : void 0
114
- }),
115
- []
116
- );
115
+ });
116
+ }, []);
117
+ async function allowedTypes(parentId2) {
118
+ if (!parentId2) {
119
+ return root.contains ?? [];
120
+ } else {
121
+ const parent = await graph.preferDraft.get(
122
+ Entry({ entryId: parentId2 }).select(parentData)
123
+ );
124
+ const parentType = parent && config.schema[parent.type];
125
+ return parentType && Type.meta(parentType).contains || keys(config.schema);
126
+ }
127
+ }
117
128
  const typeField = useMemo(() => {
118
- const result = select("Select type", {});
119
- track.options(result, async (get) => {
120
- const types = [];
121
- const selectedParent = get(parentField);
122
- const parentId2 = selectedParent?.entry;
123
- if (!parentId2) {
124
- types.push(...root.contains ?? []);
125
- } else {
126
- const parent = await graph.preferDraft.get(
127
- Entry({ entryId: parentId2 }).select(parentData)
128
- );
129
- const parentType = parent && config.schema[parent.type];
130
- types.push(
131
- ...parentType && Type.meta(parentType).contains || keys(config.schema)
132
- );
133
- }
129
+ const typeField2 = select("Select type", {});
130
+ return track.options(typeField2, async (get) => {
131
+ const selectedParent2 = get(parentField);
132
+ const parentId2 = selectedParent2?.entry;
133
+ const types = await allowedTypes(parentId2);
134
134
  return {
135
135
  items: fromEntries(
136
136
  types.map((key) => {
@@ -141,7 +141,25 @@ function NewEntryForm({ parentId }) {
141
141
  )
142
142
  };
143
143
  });
144
- return result;
144
+ }, []);
145
+ const copyFromField = useMemo(() => {
146
+ const copyFromField2 = link.entry("Copy content from");
147
+ return track.options(copyFromField2, (get) => {
148
+ const type2 = get(typeField);
149
+ return {
150
+ readOnly: !type2,
151
+ pickers: {
152
+ entry: entryPicker({
153
+ condition: Entry.type.is(type2),
154
+ withNavigation: false,
155
+ hint: void 0,
156
+ title: "Copy content from",
157
+ max: 1,
158
+ selection: entryFields
159
+ })
160
+ }
161
+ };
162
+ });
145
163
  }, []);
146
164
  const [isCreating, setIsCreating] = useState(false);
147
165
  const updateEntries = useSetAtom(changedEntriesAtom);
@@ -149,11 +167,26 @@ function NewEntryForm({ parentId }) {
149
167
  () => type({
150
168
  parent: parentField,
151
169
  title: titleField,
152
- type: typeField
170
+ type: typeField,
171
+ copyFrom: copyFromField
153
172
  }),
154
173
  []
155
174
  );
156
175
  const form = useForm(formType);
176
+ const parentAtoms = form.fieldInfo(parentField);
177
+ const typeAtoms = form.fieldInfo(typeField);
178
+ const copyFromAtoms = form.fieldInfo(copyFromField);
179
+ const selectedType = useAtomValue(typeAtoms.value);
180
+ const selectedParent = useAtomValue(parentAtoms.value);
181
+ useEffect(() => {
182
+ allowedTypes(selectedParent?.entry).then((types) => {
183
+ if (types.length > 0)
184
+ typeAtoms.mutator(types[0]);
185
+ });
186
+ }, [selectedParent]);
187
+ useEffect(() => {
188
+ copyFromAtoms.mutator.replace(void 0);
189
+ }, [selectedType]);
157
190
  async function handleCreate(e) {
158
191
  e.preventDefault();
159
192
  const { title, type: selected } = form.data();
@@ -170,7 +203,7 @@ function NewEntryForm({ parentId }) {
170
203
  phase: config.enableDrafts ? EntryPhase.Draft : EntryPhase.Published
171
204
  };
172
205
  const parentId2 = form.data().parent?.entry;
173
- const parent = await graph.preferDraft.get(
206
+ const parent = await graph.preferPublished.get(
174
207
  Entry({ entryId: parentId2 }).select(parentData)
175
208
  );
176
209
  const parentPaths = parent ? parent.parentPaths.concat(parent.path) : [];
@@ -179,6 +212,10 @@ function NewEntryForm({ parentId }) {
179
212
  const parentDir = dirname(filePath);
180
213
  const entryType = config.schema[selected];
181
214
  const url = entryUrl(entryType, { ...data, parentPaths });
215
+ const copyFrom = form.data().copyFrom?.entry;
216
+ const entryData = copyFrom ? await graph.preferPublished.get(
217
+ Entry({ entryId: copyFrom }).select(Entry.data)
218
+ ) : {};
182
219
  const entry = await createEntryRow(config, {
183
220
  entryId,
184
221
  ...data,
@@ -197,7 +234,7 @@ function NewEntryForm({ parentId }) {
197
234
  modifiedAt: Date.now(),
198
235
  active: true,
199
236
  main: false,
200
- data: { title, path },
237
+ data: { ...entryData, title, path },
201
238
  searchableText: ""
202
239
  });
203
240
  return mutate({
@@ -19,6 +19,7 @@ export interface ExplorerProps {
19
19
  toggleSelect?: (entry: ExporerItemSelect) => void;
20
20
  onNavigate?: (entryId: string) => void;
21
21
  showMedia?: boolean;
22
+ withNavigation?: boolean;
22
23
  border?: boolean;
23
24
  }
24
25
  export declare function Explorer({ type, cursor, virtualized, max, selectable, selection, toggleSelect, onNavigate, showMedia, border }: ExplorerProps): import("react/jsx-runtime").JSX.Element;
@@ -99,7 +99,7 @@ function ExplorerItem({
99
99
  ]
100
100
  }
101
101
  ),
102
- explorer.onNavigate && childrenAmount > 0 && /* @__PURE__ */ jsxs(
102
+ explorer.withNavigation && explorer.onNavigate && childrenAmount > 0 && /* @__PURE__ */ jsxs(
103
103
  "button",
104
104
  {
105
105
  type: "button",
package/dist/index.css CHANGED
@@ -225,7 +225,7 @@ button {
225
225
  /* src/dashboard/view/Create.module.scss */
226
226
  .alinea-Create {
227
227
  width: 100%;
228
- padding: 0.1875rem 0.625rem;
228
+ padding: 0.25rem 0.625rem;
229
229
  gap: 0.625rem;
230
230
  display: flex;
231
231
  flex-wrap: wrap;
@@ -582,6 +582,7 @@ button {
582
582
  min-width: 0;
583
583
  font-size: 0.8125rem;
584
584
  padding: 0 0.25rem;
585
+ min-height: 1.75rem;
585
586
  }
586
587
  .alinea-EntrySummary-thumb {
587
588
  height: 100%;
@@ -3834,7 +3835,7 @@ h3.alinea-RichTextKit-heading:not(:last-child) {
3834
3835
  display: flex;
3835
3836
  align-items: center;
3836
3837
  gap: 0.5rem;
3837
- padding: 0.625rem;
3838
+ padding: 0.375rem 0.5rem;
3838
3839
  }
3839
3840
  .alinea-Sink-title {
3840
3841
  font-size: 0.8125rem;
@@ -12,12 +12,8 @@ export interface RichTextOptions<Blocks extends Schema> extends FieldOptions<Tex
12
12
  optional?: boolean;
13
13
  /** Display a minimal version */
14
14
  inline?: boolean;
15
- /** A default value */
16
- initialValue?: TextDoc<Blocks>;
17
- /** Hide this rich text field */
18
- hidden?: boolean;
19
- /** Make this rich text field read-only */
20
- readOnly?: boolean;
15
+ /** Index the text value of this field */
16
+ searchable?: boolean;
21
17
  }
22
18
  /** Create a rich text field configuration */
23
19
  export declare function richText<Blocks extends Schema = {}>(label: string, options?: WithoutLabel<RichTextOptions<Blocks>>): RichTextField<Blocks, RichTextOptions<Blocks>>;
@@ -19,6 +19,8 @@ export interface TextOptions extends FieldOptions<string> {
19
19
  iconRight?: ComponentType;
20
20
  /** Focus this input automatically */
21
21
  autoFocus?: boolean;
22
+ /** Index the text value of this field */
23
+ searchable?: boolean;
22
24
  }
23
25
  export declare class TextField extends ScalarField<string, TextOptions> {
24
26
  }
@@ -303,7 +303,8 @@ function EntryPickerModal({
303
303
  showMedia,
304
304
  onNavigate: search ? void 0 : (entryId) => {
305
305
  setDestination({ ...destination, parentId: entryId });
306
- }
306
+ },
307
+ withNavigation
307
308
  }
308
309
  ) }) }),
309
310
  showMedia && /* @__PURE__ */ jsx(
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "bin": "./dist/cli.js",
3
3
  "name": "alinea",
4
- "version": "0.5.4",
4
+ "version": "0.5.6",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "scripts": {