@iyulab/lowline-form 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/bundle/lowline-form.bundle.js +1599 -1551
- package/dist/draft.d.ts +46 -5
- package/dist/draft.js +70 -6
- package/dist/lowline-form.d.ts +4 -0
- package/dist/lowline-form.js +16 -1
- package/dist/session.d.ts +14 -1
- package/dist/session.js +43 -7
- package/package.json +1 -1
package/dist/draft.d.ts
CHANGED
|
@@ -8,10 +8,21 @@ export interface Suggestion {
|
|
|
8
8
|
origin?: 'decider' | 'other';
|
|
9
9
|
fit?: 'exact' | 'prefix';
|
|
10
10
|
}
|
|
11
|
-
|
|
11
|
+
/** The layer of the decider an answer came from. */
|
|
12
|
+
export type Layer = 'key' | 'similar' | 'judgement';
|
|
13
|
+
/** What the host knows of the answer it shows or holds back, beyond its value: the layer that gave it and what it promised. */
|
|
14
|
+
export interface Reported {
|
|
15
|
+
layer?: Layer | null;
|
|
16
|
+
promise?: number;
|
|
17
|
+
}
|
|
18
|
+
interface Track extends Reported {
|
|
12
19
|
shown?: Suggestion;
|
|
13
20
|
declined?: boolean;
|
|
14
21
|
held?: number;
|
|
22
|
+
/** The answer held back for a check, kept so the record can say what the person was not shown. */
|
|
23
|
+
withheld?: Suggestion;
|
|
24
|
+
/** When the person reached the field, or its suggestion came into view — the first — on the session's clock, in milliseconds. */
|
|
25
|
+
at?: number;
|
|
15
26
|
}
|
|
16
27
|
/**
|
|
17
28
|
* The account of one record while it is written: the values it was opened with, and for each field the suggestion
|
|
@@ -19,17 +30,42 @@ interface Track {
|
|
|
19
30
|
* turns it, with the saved values, into what each decided field teaches — so a value taken from a suggestion is never
|
|
20
31
|
* counted as one typed. It is only bookkeeping: nothing is counted here.
|
|
21
32
|
*
|
|
22
|
-
* Written as the engine reads it: `{ opened, fields: { field: { shown, declined, held } } }
|
|
33
|
+
* Written as the engine reads it: `{ opened, fields: { field: { shown, declined, held } } }`, and — only what was
|
|
34
|
+
* reported — `opened_at`, `saved_at` (milliseconds on one steady clock), `arrival` (the fields in the order their values
|
|
35
|
+
* arrived), `by` and `batch`, and in a field `layer`, `promise`, `at` and `withheld`.
|
|
23
36
|
*/
|
|
24
37
|
export declare class Draft {
|
|
25
38
|
readonly opened: Values;
|
|
26
39
|
readonly fields: Record<string, Track>;
|
|
40
|
+
private openedAt?;
|
|
41
|
+
private savedAt?;
|
|
42
|
+
private arrival;
|
|
43
|
+
private by?;
|
|
44
|
+
private batch?;
|
|
27
45
|
/** A record opened holding `values` — none for a new one. Empty values are left out. */
|
|
28
46
|
constructor(values?: Values);
|
|
29
47
|
/** A suggestion came into view for the field. A suggestion turned down earlier stays turned down only if it is this. */
|
|
30
|
-
show(field: string, suggestion: Suggestion): void;
|
|
31
|
-
/**
|
|
32
|
-
|
|
48
|
+
show(field: string, suggestion: Suggestion, reported?: Reported): void;
|
|
49
|
+
/**
|
|
50
|
+
* The field's answer is held back for a check, drawn with `probability`: what the person settles is checked.
|
|
51
|
+
* `withheld` is the answer not shown, with its layer and promise, for the record to say what was held back.
|
|
52
|
+
*/
|
|
53
|
+
hold(field: string, probability: number, withheld?: {
|
|
54
|
+
suggestion: Suggestion;
|
|
55
|
+
} & Reported): void;
|
|
56
|
+
/** The record was opened at `ms` on the session's clock (a steady clock in milliseconds: only differences are read). */
|
|
57
|
+
openAt(ms: number): void;
|
|
58
|
+
/** The record is being saved at `ms` on the same clock. */
|
|
59
|
+
saveAt(ms: number): void;
|
|
60
|
+
/** The person reached the field, or its suggestion came into view, at `ms`. Only the first report for a field counts. */
|
|
61
|
+
reach(field: string, ms: number): void;
|
|
62
|
+
/** The field's current value arrived now: it moves to the end of the order values arrived in. */
|
|
63
|
+
arrived(field: string): void;
|
|
64
|
+
/** The record is settled by the person `id`: an opaque id, one person, one id; not a name. */
|
|
65
|
+
settledBy(id: string): void;
|
|
66
|
+
/** The record is settled in the batch `id`, with the records that came at once from the same source. */
|
|
67
|
+
inBatch(id: string): void;
|
|
68
|
+
private report;
|
|
33
69
|
/** The person turned down the suggestion in view; false, changing nothing, when none was shown. */
|
|
34
70
|
decline(field: string): boolean;
|
|
35
71
|
/** Whether `suggestion` is the one in view for the field and the person turned it down. */
|
|
@@ -39,6 +75,11 @@ export declare class Draft {
|
|
|
39
75
|
toJSON(): {
|
|
40
76
|
opened: Values;
|
|
41
77
|
fields: Record<string, Track>;
|
|
78
|
+
opened_at?: number;
|
|
79
|
+
saved_at?: number;
|
|
80
|
+
arrival?: string[];
|
|
81
|
+
by?: string;
|
|
82
|
+
batch?: string;
|
|
42
83
|
};
|
|
43
84
|
}
|
|
44
85
|
export declare function isEmpty(value: Value | undefined): boolean;
|
package/dist/draft.js
CHANGED
|
@@ -4,27 +4,79 @@
|
|
|
4
4
|
* turns it, with the saved values, into what each decided field teaches — so a value taken from a suggestion is never
|
|
5
5
|
* counted as one typed. It is only bookkeeping: nothing is counted here.
|
|
6
6
|
*
|
|
7
|
-
* Written as the engine reads it: `{ opened, fields: { field: { shown, declined, held } } }
|
|
7
|
+
* Written as the engine reads it: `{ opened, fields: { field: { shown, declined, held } } }`, and — only what was
|
|
8
|
+
* reported — `opened_at`, `saved_at` (milliseconds on one steady clock), `arrival` (the fields in the order their values
|
|
9
|
+
* arrived), `by` and `batch`, and in a field `layer`, `promise`, `at` and `withheld`.
|
|
8
10
|
*/
|
|
9
11
|
export class Draft {
|
|
10
12
|
/** A record opened holding `values` — none for a new one. Empty values are left out. */
|
|
11
13
|
constructor(values = {}) {
|
|
12
14
|
this.fields = {};
|
|
15
|
+
this.arrival = [];
|
|
13
16
|
this.opened = Object.fromEntries(Object.entries(values).filter(([, v]) => !isEmpty(v)));
|
|
14
17
|
}
|
|
15
18
|
/** A suggestion came into view for the field. A suggestion turned down earlier stays turned down only if it is this. */
|
|
16
|
-
show(field, suggestion) {
|
|
19
|
+
show(field, suggestion, reported = {}) {
|
|
17
20
|
const track = (this.fields[field] ??= {});
|
|
18
21
|
if (!sameSuggestion(track.shown, suggestion))
|
|
19
22
|
track.declined = false;
|
|
20
23
|
track.shown = { ...suggestion };
|
|
21
24
|
delete track.held;
|
|
25
|
+
delete track.withheld;
|
|
26
|
+
this.report(track, reported);
|
|
22
27
|
}
|
|
23
|
-
/**
|
|
24
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The field's answer is held back for a check, drawn with `probability`: what the person settles is checked.
|
|
30
|
+
* `withheld` is the answer not shown, with its layer and promise, for the record to say what was held back.
|
|
31
|
+
*/
|
|
32
|
+
hold(field, probability, withheld) {
|
|
25
33
|
if (!(probability > 0 && probability <= 1))
|
|
26
34
|
throw new RangeError('a check is drawn with a probability above 0 and at most 1');
|
|
27
|
-
this.fields[field]
|
|
35
|
+
const at = this.fields[field]?.at;
|
|
36
|
+
const track = { held: probability };
|
|
37
|
+
if (at !== undefined)
|
|
38
|
+
track.at = at;
|
|
39
|
+
if (withheld) {
|
|
40
|
+
track.withheld = { ...withheld.suggestion };
|
|
41
|
+
this.report(track, withheld);
|
|
42
|
+
}
|
|
43
|
+
this.fields[field] = track;
|
|
44
|
+
}
|
|
45
|
+
/** The record was opened at `ms` on the session's clock (a steady clock in milliseconds: only differences are read). */
|
|
46
|
+
openAt(ms) {
|
|
47
|
+
this.openedAt = whole(ms);
|
|
48
|
+
}
|
|
49
|
+
/** The record is being saved at `ms` on the same clock. */
|
|
50
|
+
saveAt(ms) {
|
|
51
|
+
this.savedAt = whole(ms);
|
|
52
|
+
}
|
|
53
|
+
/** The person reached the field, or its suggestion came into view, at `ms`. Only the first report for a field counts. */
|
|
54
|
+
reach(field, ms) {
|
|
55
|
+
const track = (this.fields[field] ??= {});
|
|
56
|
+
track.at ??= whole(ms);
|
|
57
|
+
}
|
|
58
|
+
/** The field's current value arrived now: it moves to the end of the order values arrived in. */
|
|
59
|
+
arrived(field) {
|
|
60
|
+
this.arrival = this.arrival.filter((f) => f !== field);
|
|
61
|
+
this.arrival.push(field);
|
|
62
|
+
}
|
|
63
|
+
/** The record is settled by the person `id`: an opaque id, one person, one id; not a name. */
|
|
64
|
+
settledBy(id) {
|
|
65
|
+
this.by = id;
|
|
66
|
+
}
|
|
67
|
+
/** The record is settled in the batch `id`, with the records that came at once from the same source. */
|
|
68
|
+
inBatch(id) {
|
|
69
|
+
this.batch = id;
|
|
70
|
+
}
|
|
71
|
+
report(track, { layer, promise }) {
|
|
72
|
+
if (layer)
|
|
73
|
+
track.layer = layer;
|
|
74
|
+
else
|
|
75
|
+
delete track.layer;
|
|
76
|
+
if (promise !== undefined)
|
|
77
|
+
track.promise = promise;
|
|
78
|
+
else
|
|
79
|
+
delete track.promise;
|
|
28
80
|
}
|
|
29
81
|
/** The person turned down the suggestion in view; false, changing nothing, when none was shown. */
|
|
30
82
|
decline(field) {
|
|
@@ -46,9 +98,21 @@ export class Draft {
|
|
|
46
98
|
return this.fields[field]?.held;
|
|
47
99
|
}
|
|
48
100
|
toJSON() {
|
|
49
|
-
return {
|
|
101
|
+
return {
|
|
102
|
+
opened: this.opened,
|
|
103
|
+
fields: this.fields,
|
|
104
|
+
...(this.openedAt !== undefined ? { opened_at: this.openedAt } : {}),
|
|
105
|
+
...(this.savedAt !== undefined ? { saved_at: this.savedAt } : {}),
|
|
106
|
+
...(this.arrival.length > 0 ? { arrival: this.arrival } : {}),
|
|
107
|
+
...(this.by !== undefined ? { by: this.by } : {}),
|
|
108
|
+
...(this.batch !== undefined ? { batch: this.batch } : {}),
|
|
109
|
+
};
|
|
50
110
|
}
|
|
51
111
|
}
|
|
112
|
+
/** A time the engine reads: whole milliseconds, never negative. */
|
|
113
|
+
function whole(ms) {
|
|
114
|
+
return Math.max(0, Math.round(ms));
|
|
115
|
+
}
|
|
52
116
|
function sameSuggestion(a, b) {
|
|
53
117
|
return a !== undefined && a.value === b.value && (a.origin ?? 'decider') === (b.origin ?? 'decider') && (a.fit ?? 'exact') === (b.fit ?? 'exact');
|
|
54
118
|
}
|
package/dist/lowline-form.d.ts
CHANGED
|
@@ -29,6 +29,10 @@ export declare class LowlineForm extends LitElement {
|
|
|
29
29
|
rate: number;
|
|
30
30
|
/** The words the form shows, in the host's language. */
|
|
31
31
|
strings: Strings;
|
|
32
|
+
/** Who is writing, as an opaque id the host gives — one person, one id; not a name. Sent with the save when set. */
|
|
33
|
+
settledBy: string;
|
|
34
|
+
/** The batch the record is written in, when several come at once from one source. Sent with the save when set. */
|
|
35
|
+
batch: string;
|
|
32
36
|
private formdown?;
|
|
33
37
|
private session?;
|
|
34
38
|
private asking?;
|
package/dist/lowline-form.js
CHANGED
|
@@ -41,11 +41,16 @@ let LowlineForm = class LowlineForm extends LitElement {
|
|
|
41
41
|
this.rate = 0.05;
|
|
42
42
|
/** The words the form shows, in the host's language. */
|
|
43
43
|
this.strings = englishStrings;
|
|
44
|
+
/** Who is writing, as an opaque id the host gives — one person, one id; not a name. Sent with the save when set. */
|
|
45
|
+
this.settledBy = '';
|
|
46
|
+
/** The batch the record is written in, when several come at once from one source. Sent with the save when set. */
|
|
47
|
+
this.batch = '';
|
|
44
48
|
this.client = new LowlineClient(this.src);
|
|
45
49
|
this.fieldStates = {};
|
|
46
50
|
/** Whether the host judges what the records could not answer — until its `judge` route says it has no model. */
|
|
47
51
|
this.canJudge = true;
|
|
48
52
|
this.changed = () => {
|
|
53
|
+
this.session?.took(this.values());
|
|
49
54
|
clearTimeout(this.asking);
|
|
50
55
|
this.asking = setTimeout(() => void this.ask(), ASK_DELAY_MS);
|
|
51
56
|
};
|
|
@@ -114,6 +119,8 @@ let LowlineForm = class LowlineForm extends LitElement {
|
|
|
114
119
|
if (!this.session || !this.form || !this.record)
|
|
115
120
|
throw new Error('lowline-form needs a form and a record to save');
|
|
116
121
|
const values = this.values();
|
|
122
|
+
this.session.took(values);
|
|
123
|
+
this.session.beforeSave(this.settledBy || undefined, this.batch || undefined);
|
|
117
124
|
const saved = await this.client.settle(this.form, this.record, this.session.draft, sendValues ? values : undefined);
|
|
118
125
|
this.session.saved(values);
|
|
119
126
|
this.dispatchEvent(new CustomEvent('lowline-saved', { detail: { ...saved, values }, bubbles: true, composed: true }));
|
|
@@ -157,8 +164,10 @@ let LowlineForm = class LowlineForm extends LitElement {
|
|
|
157
164
|
const formdown = this.formdown;
|
|
158
165
|
if (!formdown)
|
|
159
166
|
return;
|
|
160
|
-
if (Object.keys(round.prefill).length > 0)
|
|
167
|
+
if (Object.keys(round.prefill).length > 0) {
|
|
161
168
|
formdown.updateData({ ...formdown.data, ...round.prefill });
|
|
169
|
+
session.took(this.values());
|
|
170
|
+
}
|
|
162
171
|
formdown.fieldStates = this.fieldStates;
|
|
163
172
|
if (this.canJudge)
|
|
164
173
|
void this.judge(session, values, shown);
|
|
@@ -213,6 +222,12 @@ __decorate([
|
|
|
213
222
|
__decorate([
|
|
214
223
|
property({ attribute: false })
|
|
215
224
|
], LowlineForm.prototype, "strings", void 0);
|
|
225
|
+
__decorate([
|
|
226
|
+
property({ attribute: 'settled-by' })
|
|
227
|
+
], LowlineForm.prototype, "settledBy", void 0);
|
|
228
|
+
__decorate([
|
|
229
|
+
property()
|
|
230
|
+
], LowlineForm.prototype, "batch", void 0);
|
|
216
231
|
__decorate([
|
|
217
232
|
query('formdown-ui')
|
|
218
233
|
], LowlineForm.prototype, "formdown", void 0);
|
package/dist/session.d.ts
CHANGED
|
@@ -68,6 +68,8 @@ export declare class Session {
|
|
|
68
68
|
readonly rate: number;
|
|
69
69
|
private readonly random;
|
|
70
70
|
private readonly strings;
|
|
71
|
+
/** A steady clock in milliseconds (`performance.now()` by default): the record's times are read from it. */
|
|
72
|
+
private readonly clock;
|
|
71
73
|
draft: Draft;
|
|
72
74
|
private drawn;
|
|
73
75
|
/** Fields whose value the form put in unasked, from the suggestion still shown. */
|
|
@@ -76,7 +78,18 @@ export declare class Session {
|
|
|
76
78
|
private written;
|
|
77
79
|
/** The judgement asked for each field, by what it reads; `null` while the host is still judging. */
|
|
78
80
|
private judgements;
|
|
79
|
-
|
|
81
|
+
/** The values the form held when it was last looked at, to tell which fields' values arrived since. */
|
|
82
|
+
private seen;
|
|
83
|
+
constructor(id: string, opened: Values, rate?: number, random?: () => number, strings?: Strings,
|
|
84
|
+
/** A steady clock in milliseconds (`performance.now()` by default): the record's times are read from it. */
|
|
85
|
+
clock?: () => number);
|
|
86
|
+
/**
|
|
87
|
+
* The form's values are `values` now: each field whose value is new since the last look arrived, and the person has
|
|
88
|
+
* reached it. A value the form put in unasked arrives too — it is when the record came to hold it.
|
|
89
|
+
*/
|
|
90
|
+
took(values: Values): void;
|
|
91
|
+
/** The record is about to be saved by `by`, in `batch`: the draft says so, with the time. */
|
|
92
|
+
beforeSave(by?: string, batch?: string): void;
|
|
80
93
|
/**
|
|
81
94
|
* Takes a round of proposals for the form's `values` now. A field offered a value from the person's writing keeps it
|
|
82
95
|
* in view until the person picks it or turns it down.
|
package/dist/session.js
CHANGED
|
@@ -12,11 +12,14 @@ export const englishStrings = {
|
|
|
12
12
|
* is looked at, and only for a value the decider suggests.
|
|
13
13
|
*/
|
|
14
14
|
export class Session {
|
|
15
|
-
constructor(id, opened, rate = 0.05, random = Math.random, strings = englishStrings
|
|
15
|
+
constructor(id, opened, rate = 0.05, random = Math.random, strings = englishStrings,
|
|
16
|
+
/** A steady clock in milliseconds (`performance.now()` by default): the record's times are read from it. */
|
|
17
|
+
clock = () => performance.now()) {
|
|
16
18
|
this.id = id;
|
|
17
19
|
this.rate = rate;
|
|
18
20
|
this.random = random;
|
|
19
21
|
this.strings = strings;
|
|
22
|
+
this.clock = clock;
|
|
20
23
|
this.drawn = new Set();
|
|
21
24
|
/** Fields whose value the form put in unasked, from the suggestion still shown. */
|
|
22
25
|
this.filled = new Set();
|
|
@@ -25,6 +28,30 @@ export class Session {
|
|
|
25
28
|
/** The judgement asked for each field, by what it reads; `null` while the host is still judging. */
|
|
26
29
|
this.judgements = new Map();
|
|
27
30
|
this.draft = new Draft(opened);
|
|
31
|
+
this.draft.openAt(this.clock());
|
|
32
|
+
this.seen = { ...this.draft.opened };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* The form's values are `values` now: each field whose value is new since the last look arrived, and the person has
|
|
36
|
+
* reached it. A value the form put in unasked arrives too — it is when the record came to hold it.
|
|
37
|
+
*/
|
|
38
|
+
took(values) {
|
|
39
|
+
const now = this.clock();
|
|
40
|
+
for (const [field, value] of Object.entries(values)) {
|
|
41
|
+
if (isEmpty(value) || sameValue(this.seen[field], value))
|
|
42
|
+
continue;
|
|
43
|
+
this.draft.arrived(field);
|
|
44
|
+
this.draft.reach(field, now);
|
|
45
|
+
}
|
|
46
|
+
this.seen = { ...values };
|
|
47
|
+
}
|
|
48
|
+
/** The record is about to be saved by `by`, in `batch`: the draft says so, with the time. */
|
|
49
|
+
beforeSave(by, batch) {
|
|
50
|
+
this.draft.saveAt(this.clock());
|
|
51
|
+
if (by)
|
|
52
|
+
this.draft.settledBy(by);
|
|
53
|
+
if (batch)
|
|
54
|
+
this.draft.inBatch(batch);
|
|
28
55
|
}
|
|
29
56
|
/**
|
|
30
57
|
* Takes a round of proposals for the form's `values` now. A field offered a value from the person's writing keeps it
|
|
@@ -32,9 +59,9 @@ export class Session {
|
|
|
32
59
|
*/
|
|
33
60
|
apply(proposals, values) {
|
|
34
61
|
const round = { fieldStates: Object.fromEntries(this.written), prefill: {} };
|
|
35
|
-
for (const
|
|
36
|
-
if (suggestion && !this.written.has(field))
|
|
37
|
-
this.draw(
|
|
62
|
+
for (const proposal of proposals) {
|
|
63
|
+
if (proposal.suggestion && !this.written.has(proposal.field))
|
|
64
|
+
this.draw(proposal);
|
|
38
65
|
}
|
|
39
66
|
// A field held back for a check holds back the open fields of its group: the person settling it with nothing in view
|
|
40
67
|
// is not shown what is suggested alongside it. Only the held field is the check.
|
|
@@ -49,7 +76,8 @@ export class Session {
|
|
|
49
76
|
// Turned down, it stays down for this record: neither shown again nor put back in.
|
|
50
77
|
if (this.draft.declined(field, suggestion))
|
|
51
78
|
continue;
|
|
52
|
-
this.draft.show(field, suggestion);
|
|
79
|
+
this.draft.show(field, suggestion, proposal);
|
|
80
|
+
this.draft.reach(field, this.clock());
|
|
53
81
|
if (proposal.prefill && isEmpty(values[field])) {
|
|
54
82
|
round.prefill[field] = suggestion.value;
|
|
55
83
|
this.filled.add(field);
|
|
@@ -139,13 +167,18 @@ export class Session {
|
|
|
139
167
|
/** The record was saved holding `values`: what is written next starts from them. */
|
|
140
168
|
saved(values) {
|
|
141
169
|
this.draft = new Draft(values);
|
|
170
|
+
this.draft.openAt(this.clock());
|
|
171
|
+
this.seen = { ...this.draft.opened };
|
|
142
172
|
this.drawn.clear();
|
|
143
173
|
this.filled.clear();
|
|
144
174
|
this.written.clear();
|
|
145
175
|
this.judgements.clear();
|
|
146
176
|
}
|
|
147
177
|
/** Whether the field's answer is held back now — drawn the first time a checkable value is proposed for it. */
|
|
148
|
-
draw(
|
|
178
|
+
draw(proposal) {
|
|
179
|
+
const { field, suggestion } = proposal;
|
|
180
|
+
if (!suggestion)
|
|
181
|
+
return false;
|
|
149
182
|
if (this.drawn.has(field))
|
|
150
183
|
return this.draft.held(field) !== undefined;
|
|
151
184
|
const checkable = (suggestion.origin ?? 'decider') === 'decider' && (suggestion.fit ?? 'exact') === 'exact';
|
|
@@ -153,7 +186,7 @@ export class Session {
|
|
|
153
186
|
return false;
|
|
154
187
|
this.drawn.add(field);
|
|
155
188
|
if (this.rate > 0 && this.rate <= 1 && this.random() < this.rate) {
|
|
156
|
-
this.draft.hold(field, this.rate);
|
|
189
|
+
this.draft.hold(field, this.rate, { ...proposal, suggestion });
|
|
157
190
|
return true;
|
|
158
191
|
}
|
|
159
192
|
return false;
|
|
@@ -173,6 +206,9 @@ export class Session {
|
|
|
173
206
|
return undefined;
|
|
174
207
|
}
|
|
175
208
|
}
|
|
209
|
+
function sameValue(a, b) {
|
|
210
|
+
return JSON.stringify(a ?? '') === JSON.stringify(b);
|
|
211
|
+
}
|
|
176
212
|
/** What a proposal asking for a judgement reads, as one text. */
|
|
177
213
|
function reads(proposal) {
|
|
178
214
|
return JSON.stringify(proposal.asks ?? []);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iyulab/lowline-form",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "The Lowline form element: a formdown form whose decided fields are suggested from the records people confirmed, talking to a Lowline host's routes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|