@graffiti-garden/implementation-decentralized 0.0.2 → 0.0.4

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 (48) hide show
  1. package/dist/1-services/4-inboxes-tests.d.ts.map +1 -1
  2. package/dist/1-services/4-inboxes.d.ts +3 -3
  3. package/dist/1-services/4-inboxes.d.ts.map +1 -1
  4. package/dist/3-protocol/3-object-encoding.d.ts.map +1 -1
  5. package/dist/3-protocol/4-graffiti.d.ts +2 -1
  6. package/dist/3-protocol/4-graffiti.d.ts.map +1 -1
  7. package/dist/3-protocol/login-dialog.html.d.ts +1 -1
  8. package/dist/3-protocol/login-dialog.html.d.ts.map +1 -1
  9. package/dist/browser/index.js +7 -7
  10. package/dist/browser/index.js.map +3 -3
  11. package/dist/browser/login-dialog.html-VTDKJZBG.js +44 -0
  12. package/dist/browser/login-dialog.html-VTDKJZBG.js.map +7 -0
  13. package/dist/browser/{style-YUTCEBZV-RWYJV575.js → style-RMTPI5KV-Y5KAOOZR.js} +19 -36
  14. package/dist/browser/style-RMTPI5KV-Y5KAOOZR.js.map +7 -0
  15. package/dist/cjs/1-services/4-inboxes-tests.js +2 -0
  16. package/dist/cjs/1-services/4-inboxes-tests.js.map +2 -2
  17. package/dist/cjs/1-services/4-inboxes.js +17 -8
  18. package/dist/cjs/1-services/4-inboxes.js.map +2 -2
  19. package/dist/cjs/3-protocol/1-sessions.js +1 -1
  20. package/dist/cjs/3-protocol/1-sessions.js.map +2 -2
  21. package/dist/cjs/3-protocol/3-object-encoding.js +3 -2
  22. package/dist/cjs/3-protocol/3-object-encoding.js.map +2 -2
  23. package/dist/cjs/3-protocol/4-graffiti.js +193 -135
  24. package/dist/cjs/3-protocol/4-graffiti.js.map +3 -3
  25. package/dist/cjs/3-protocol/login-dialog.html.js +9 -9
  26. package/dist/cjs/3-protocol/login-dialog.html.js.map +1 -1
  27. package/dist/esm/1-services/4-inboxes-tests.js +2 -0
  28. package/dist/esm/1-services/4-inboxes-tests.js.map +2 -2
  29. package/dist/esm/1-services/4-inboxes.js +19 -9
  30. package/dist/esm/1-services/4-inboxes.js.map +2 -2
  31. package/dist/esm/3-protocol/1-sessions.js +1 -1
  32. package/dist/esm/3-protocol/1-sessions.js.map +2 -2
  33. package/dist/esm/3-protocol/3-object-encoding.js +3 -2
  34. package/dist/esm/3-protocol/3-object-encoding.js.map +2 -2
  35. package/dist/esm/3-protocol/4-graffiti.js +194 -135
  36. package/dist/esm/3-protocol/4-graffiti.js.map +3 -3
  37. package/dist/esm/3-protocol/login-dialog.html.js +9 -9
  38. package/dist/esm/3-protocol/login-dialog.html.js.map +1 -1
  39. package/package.json +7 -7
  40. package/src/1-services/4-inboxes-tests.ts +2 -0
  41. package/src/1-services/4-inboxes.ts +25 -15
  42. package/src/3-protocol/1-sessions.ts +1 -1
  43. package/src/3-protocol/3-object-encoding.ts +4 -2
  44. package/src/3-protocol/4-graffiti.ts +260 -170
  45. package/src/3-protocol/login-dialog.html.ts +9 -9
  46. package/dist/browser/login-dialog.html-XUWYDNNI.js +0 -44
  47. package/dist/browser/login-dialog.html-XUWYDNNI.js.map +0 -7
  48. package/dist/browser/style-YUTCEBZV-RWYJV575.js.map +0 -7
@@ -29,10 +29,12 @@ import { Authorization } from "../1-services/1-authorization";
29
29
  import { StorageBuckets } from "../1-services/3-storage-buckets";
30
30
  import {
31
31
  Inboxes,
32
+ LABELED_MESSAGE_LABEL_KEY,
32
33
  LABELED_MESSAGE_MESSAGE_KEY,
33
34
  MESSAGE_METADATA_KEY,
34
35
  MESSAGE_OBJECT_KEY,
35
36
  MESSAGE_TAGS_KEY,
37
+ type LabeledMessage,
36
38
  type MessageStream,
37
39
  } from "../1-services/4-inboxes";
38
40
 
@@ -130,6 +132,8 @@ export interface GraffitiDecentralizedOptions {
130
132
  defaultInboxEndpoints?: string[];
131
133
  }
132
134
 
135
+ const CONCURRENCY = 16;
136
+
133
137
  export class GraffitiDecentralized implements Graffiti {
134
138
  protected readonly dids = new DecentralizedIdentifiers();
135
139
  protected readonly authorization = new Authorization();
@@ -239,11 +243,7 @@ export class GraffitiDecentralized implements Graffiti {
239
243
  ) as HTMLInputElement | null;
240
244
  input?.setAttribute("value", proposedHandle);
241
245
  input?.addEventListener("focus", () => input?.select());
242
- new Promise<void>((r) => {
243
- setTimeout(() => r(), 0);
244
- }).then(() => {
245
- input?.focus();
246
- });
246
+ setTimeout(() => input?.focus(), 0);
247
247
 
248
248
  template
249
249
  ?.querySelector("#graffiti-login-handle-form")
@@ -293,13 +293,16 @@ export class GraffitiDecentralized implements Graffiti {
293
293
  e.preventDefault();
294
294
  this.login_("");
295
295
  });
296
- new Promise<void>((r) => {
297
- setTimeout(() => r(), 0);
298
- }).then(() => {
299
- (
300
- template?.querySelector("#graffiti-login-new") as HTMLAnchorElement
301
- )?.focus();
302
- });
296
+
297
+ setTimeout(
298
+ () =>
299
+ (
300
+ template?.querySelector(
301
+ "#graffiti-login-new",
302
+ ) as HTMLAnchorElement
303
+ )?.focus(),
304
+ 0,
305
+ );
303
306
  }
304
307
 
305
308
  const createUrl = new URL(this.identityCreatorEndpoint);
@@ -785,8 +788,6 @@ export class GraffitiDecentralized implements Graffiti {
785
788
  channels,
786
789
  cursors,
787
790
  } satisfies infer_<typeof CursorSchema>),
788
- continue: (session) =>
789
- this.discoverMeta<Schema>(channels, schema, cursors, session),
790
791
  };
791
792
  }
792
793
 
@@ -795,6 +796,7 @@ export class GraffitiDecentralized implements Graffiti {
795
796
  return this.discoverMeta<(typeof args)[1]>(channels, schema, {}, session);
796
797
  };
797
798
 
799
+ // @ts-ignore
798
800
  continueDiscover: Graffiti["continueDiscover"] = (...args) => {
799
801
  const [cursor, session] = args;
800
802
  // Extract the channels from the cursor
@@ -1009,189 +1011,277 @@ export class GraffitiDecentralized implements Graffiti {
1009
1011
  inboxToken,
1010
1012
  ) as unknown as MessageStream<Schema>);
1011
1013
 
1014
+ const inFlight: Promise<SingleEndpointQueryResult<Schema> | void>[] = [];
1015
+ let doneValue: string | null = null;
1016
+
1012
1017
  while (true) {
1013
- const itResult = await iterator.next();
1014
- // Return the cursor if done
1015
- if (itResult.done) return itResult.value;
1018
+ while (doneValue === null && inFlight.length < CONCURRENCY) {
1019
+ const itResult = await iterator.next();
1020
+ if (itResult.done) {
1021
+ doneValue = itResult.value;
1022
+ break;
1023
+ }
1024
+
1025
+ const processPromise = this.processOneLabeledMessage<Schema>(
1026
+ inboxEndpoint,
1027
+ itResult.value,
1028
+ inboxToken,
1029
+ recipient,
1030
+ ).catch((e) => {
1031
+ throw e;
1032
+ });
1016
1033
 
1017
- const result = itResult.value;
1034
+ inFlight.push(processPromise);
1035
+ }
1018
1036
 
1019
- const label = result.l;
1020
- if (label !== MESSAGE_LABEL_VALID && label !== MESSAGE_LABEL_UNLABELED)
1021
- continue;
1037
+ const nextProcessedPromise = inFlight.shift();
1022
1038
 
1023
- const messageId = result.id;
1024
- const { o: object, m: metadataBytes, t: receivedTags } = result.m;
1039
+ if (!nextProcessedPromise) {
1040
+ if (doneValue !== null) return doneValue;
1025
1041
 
1026
- let metadata: MessageMetadata;
1027
- try {
1028
- const metadataRaw = dagCborDecode(metadataBytes);
1029
- metadata = MessageMetadataSchema.parse(metadataRaw);
1030
- } catch (e) {
1042
+ throw new Error("Process queue empty but no return value");
1043
+ }
1044
+
1045
+ const processed = await nextProcessedPromise;
1046
+ if (processed) yield processed;
1047
+ }
1048
+ }
1049
+
1050
+ protected async processOneLabeledMessage<Schema extends JSONSchema>(
1051
+ inboxEndpoint: string,
1052
+ result: LabeledMessage<Schema>,
1053
+ inboxToken?: string | null,
1054
+ recipient?: string | null,
1055
+ ): Promise<SingleEndpointQueryResult<Schema> | void> {
1056
+ const label = result.l;
1057
+ // Anything invalid or unexpected, we can skip
1058
+ if (
1059
+ label !== MESSAGE_LABEL_VALID &&
1060
+ label !== MESSAGE_LABEL_UNLABELED &&
1061
+ label !== MESSAGE_LABEL_TRASH
1062
+ )
1063
+ return;
1064
+
1065
+ const messageId = result.id;
1066
+ const { o: object, m: metadataBytes, t: receivedTags } = result.m;
1067
+
1068
+ let metadata: MessageMetadata;
1069
+ try {
1070
+ const metadataRaw = dagCborDecode(metadataBytes);
1071
+ metadata = MessageMetadataSchema.parse(metadataRaw);
1072
+ } catch (e) {
1073
+ this.inboxes.label(
1074
+ inboxEndpoint,
1075
+ messageId,
1076
+ MESSAGE_LABEL_INVALID,
1077
+ inboxToken,
1078
+ );
1079
+ return;
1080
+ }
1081
+
1082
+ const {
1083
+ [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
1084
+ [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId,
1085
+ } = metadata;
1086
+
1087
+ const allowedTickets =
1088
+ MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata
1089
+ ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY]
1090
+ : undefined;
1091
+ const announcements =
1092
+ MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata
1093
+ ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY]
1094
+ : undefined;
1095
+
1096
+ if (label === MESSAGE_LABEL_VALID) {
1097
+ return {
1098
+ messageId,
1099
+ object,
1100
+ storageBucketKey,
1101
+ allowedTickets,
1102
+ tags: receivedTags,
1103
+ announcements,
1104
+ };
1105
+ } else if (label === MESSAGE_LABEL_TRASH) {
1106
+ // If it is simply trash, just continue.
1107
+ if (!tombstonedMessageId) return;
1108
+
1109
+ // Make sure the tombstone points to a real message
1110
+ const past = await this.inboxes.get(
1111
+ inboxEndpoint,
1112
+ tombstonedMessageId,
1113
+ inboxToken,
1114
+ );
1115
+ if (
1116
+ !past ||
1117
+ past[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url !== object.url
1118
+ )
1119
+ return;
1120
+
1121
+ // If the referred to message isn't labeled as trash, trash it
1122
+ // This may happen if a trash message is processed on another
1123
+ // device and the device cache is out of date.
1124
+ if (past[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH) {
1125
+ // Label the message as trash
1031
1126
  this.inboxes.label(
1032
1127
  inboxEndpoint,
1033
- messageId,
1034
- MESSAGE_LABEL_INVALID,
1128
+ tombstonedMessageId,
1129
+ MESSAGE_LABEL_TRASH,
1035
1130
  inboxToken,
1036
1131
  );
1037
- continue;
1038
1132
  }
1039
1133
 
1040
- const {
1041
- [MESSAGE_DATA_STORAGE_BUCKET_KEY]: storageBucketKey,
1042
- [MESSAGE_DATA_TOMBSTONED_MESSAGE_ID_KEY]: tombstonedMessageId,
1043
- } = metadata;
1044
-
1045
- const allowedTickets =
1046
- MESSAGE_DATA_ALLOWED_TICKETS_KEY in metadata
1047
- ? metadata[MESSAGE_DATA_ALLOWED_TICKETS_KEY]
1048
- : undefined;
1049
- const announcements =
1050
- MESSAGE_DATA_ANNOUNCEMENTS_KEY in metadata
1051
- ? metadata[MESSAGE_DATA_ANNOUNCEMENTS_KEY]
1052
- : undefined;
1053
-
1054
- if (label === MESSAGE_LABEL_VALID) {
1055
- yield {
1056
- messageId,
1057
- object,
1058
- storageBucketKey,
1059
- allowedTickets,
1060
- tags: receivedTags,
1061
- announcements,
1062
- };
1063
- continue;
1064
- }
1134
+ // Return the tombstone
1135
+ return {
1136
+ messageId,
1137
+ tombstone: true,
1138
+ object,
1139
+ storageBucketKey,
1140
+ allowedTickets,
1141
+ tags: receivedTags,
1142
+ announcements,
1143
+ };
1144
+ }
1065
1145
 
1066
- // Try to validate the object
1067
- let validationError: unknown | undefined = undefined;
1068
- try {
1069
- const actor = object.actor;
1070
- const actorDocument = await this.dids.resolve(actor);
1071
- const storageBucketService = actorDocument?.service?.find(
1072
- (service) =>
1073
- service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&
1074
- service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,
1146
+ // Otherwise, unlabeled: try to validate the object
1147
+ let validationError: unknown | undefined = undefined;
1148
+ try {
1149
+ const actor = object.actor;
1150
+ const actorDocument = await this.dids.resolve(actor);
1151
+ const storageBucketService = actorDocument?.service?.find(
1152
+ (service) =>
1153
+ service.id === DID_SERVICE_ID_GRAFFITI_STORAGE_BUCKET &&
1154
+ service.type === DID_SERVICE_TYPE_GRAFFITI_STORAGE_BUCKET,
1155
+ );
1156
+ if (!storageBucketService) {
1157
+ throw new GraffitiErrorNotFound(
1158
+ `Actor ${actor} has no storage bucket service`,
1075
1159
  );
1076
- if (!storageBucketService) {
1077
- throw new GraffitiErrorNotFound(
1078
- `Actor ${actor} has no storage bucket service`,
1079
- );
1080
- }
1081
- if (typeof storageBucketService.serviceEndpoint !== "string") {
1082
- throw new GraffitiErrorNotFound(
1083
- `Actor ${actor} does not have a valid storage bucket endpoint`,
1084
- );
1085
- }
1086
- const storageBucketEndpoint = storageBucketService.serviceEndpoint;
1087
-
1088
- const objectBytes = await this.storageBuckets.get(
1089
- storageBucketEndpoint,
1090
- storageBucketKey,
1091
- MAX_OBJECT_SIZE_BYTES,
1160
+ }
1161
+ if (typeof storageBucketService.serviceEndpoint !== "string") {
1162
+ throw new GraffitiErrorNotFound(
1163
+ `Actor ${actor} does not have a valid storage bucket endpoint`,
1092
1164
  );
1165
+ }
1166
+ const storageBucketEndpoint = storageBucketService.serviceEndpoint;
1093
1167
 
1094
- if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
1095
- throw new GraffitiErrorForbidden(
1096
- `Recipient is required when allowed ticket is present`,
1097
- );
1098
- }
1099
- const privateObjectInfo = allowedTickets
1100
- ? { allowedTickets }
1101
- : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata
1102
- ? {
1103
- recipient: recipient ?? "null",
1104
- allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
1105
- allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY],
1106
- }
1107
- : undefined;
1168
+ const objectBytes = await this.storageBuckets.get(
1169
+ storageBucketEndpoint,
1170
+ storageBucketKey,
1171
+ MAX_OBJECT_SIZE_BYTES,
1172
+ );
1108
1173
 
1109
- await this.objectEncoding.validate(
1110
- object,
1111
- receivedTags,
1112
- objectBytes,
1113
- privateObjectInfo,
1174
+ if (MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata && !recipient) {
1175
+ throw new GraffitiErrorForbidden(
1176
+ `Recipient is required when allowed ticket is present`,
1114
1177
  );
1115
- } catch (e) {
1116
- validationError = e;
1117
1178
  }
1179
+ const privateObjectInfo = allowedTickets
1180
+ ? { allowedTickets }
1181
+ : MESSAGE_DATA_ALLOWED_TICKET_KEY in metadata
1182
+ ? {
1183
+ recipient: recipient ?? "null",
1184
+ allowedTicket: metadata[MESSAGE_DATA_ALLOWED_TICKET_KEY],
1185
+ allowedIndex: metadata[MESSAGE_DATA_ALLOWED_TICKET_INDEX_KEY],
1186
+ }
1187
+ : undefined;
1118
1188
 
1119
- if (tombstonedMessageId) {
1120
- if (validationError instanceof GraffitiErrorNotFound) {
1121
- // Not found == The tombstone is correct
1122
- this.inboxes
1123
- // Get the referenced message
1124
- .get(inboxEndpoint, tombstonedMessageId, inboxToken)
1125
- .then((result) => {
1126
- // Make sure that it actually references the object being deleted
1127
- if (
1128
- result &&
1129
- result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url ===
1130
- object.url
1131
- ) {
1132
- // If it does, label the message as trash, it is no longer needed
1133
- this.inboxes.label(
1134
- inboxEndpoint,
1135
- tombstonedMessageId,
1136
- MESSAGE_LABEL_TRASH,
1137
- inboxToken,
1138
- );
1139
- }
1189
+ await this.objectEncoding.validate(
1190
+ object,
1191
+ receivedTags,
1192
+ objectBytes,
1193
+ privateObjectInfo,
1194
+ );
1195
+ } catch (e) {
1196
+ validationError = e;
1197
+ }
1140
1198
 
1141
- // Then, label the tombstone message as trash
1199
+ if (tombstonedMessageId) {
1200
+ if (validationError instanceof GraffitiErrorNotFound) {
1201
+ // Not found == The tombstone is correct
1202
+ this.inboxes
1203
+ // Get the referenced message
1204
+ .get(inboxEndpoint, tombstonedMessageId, inboxToken)
1205
+ .then((result) => {
1206
+ if (
1207
+ // Make sure that it actually references the object being deleted
1208
+ result &&
1209
+ result[LABELED_MESSAGE_MESSAGE_KEY][MESSAGE_OBJECT_KEY].url ===
1210
+ object.url &&
1211
+ // And that the object is not already marked as trash
1212
+ result[LABELED_MESSAGE_LABEL_KEY] !== MESSAGE_LABEL_TRASH
1213
+ ) {
1214
+ // If valid but not yet trash, label the message as trash
1142
1215
  this.inboxes.label(
1143
1216
  inboxEndpoint,
1144
- messageId,
1217
+ tombstonedMessageId,
1145
1218
  MESSAGE_LABEL_TRASH,
1146
1219
  inboxToken,
1147
1220
  );
1148
- });
1221
+ }
1149
1222
 
1150
- yield {
1151
- messageId,
1152
- tombstone: true,
1153
- object,
1154
- storageBucketKey,
1155
- allowedTickets,
1156
- tags: receivedTags,
1157
- announcements,
1158
- };
1159
- } else {
1160
- console.error("Recieved an incorrect object");
1161
- console.error(validationError);
1162
- this.inboxes.label(
1163
- inboxEndpoint,
1164
- messageId,
1165
- MESSAGE_LABEL_INVALID,
1166
- inboxToken,
1167
- );
1168
- }
1223
+ // Then, label the tombstone message as trash
1224
+ this.inboxes.label(
1225
+ inboxEndpoint,
1226
+ messageId,
1227
+ MESSAGE_LABEL_TRASH,
1228
+ inboxToken,
1229
+ );
1230
+ });
1231
+
1232
+ return {
1233
+ messageId,
1234
+ tombstone: true,
1235
+ object,
1236
+ storageBucketKey,
1237
+ allowedTickets,
1238
+ tags: receivedTags,
1239
+ announcements,
1240
+ };
1169
1241
  } else {
1170
- if (validationError === undefined) {
1171
- this.inboxes.label(
1172
- inboxEndpoint,
1173
- messageId,
1174
- MESSAGE_LABEL_VALID,
1175
- inboxToken,
1176
- );
1177
- yield {
1178
- messageId,
1179
- object,
1180
- storageBucketKey,
1181
- tags: receivedTags,
1182
- allowedTickets,
1183
- announcements,
1184
- };
1185
- } else {
1186
- console.error("Recieved an incorrect object");
1187
- console.error(validationError);
1188
- this.inboxes.label(
1189
- inboxEndpoint,
1190
- messageId,
1191
- MESSAGE_LABEL_INVALID,
1192
- inboxToken,
1193
- );
1194
- }
1242
+ console.error("Recieved an incorrect tombstone object");
1243
+ console.error(validationError);
1244
+ this.inboxes.label(
1245
+ inboxEndpoint,
1246
+ messageId,
1247
+ MESSAGE_LABEL_INVALID,
1248
+ inboxToken,
1249
+ );
1250
+ }
1251
+ } else {
1252
+ if (validationError === undefined) {
1253
+ this.inboxes.label(
1254
+ inboxEndpoint,
1255
+ messageId,
1256
+ MESSAGE_LABEL_VALID,
1257
+ inboxToken,
1258
+ );
1259
+ return {
1260
+ messageId,
1261
+ object,
1262
+ storageBucketKey,
1263
+ tags: receivedTags,
1264
+ allowedTickets,
1265
+ announcements,
1266
+ };
1267
+ } else if (validationError instanceof GraffitiErrorNotFound) {
1268
+ // Item was deleted before we got a chance to
1269
+ // validate it. Just label the message as trash.
1270
+ this.inboxes.label(
1271
+ inboxEndpoint,
1272
+ messageId,
1273
+ MESSAGE_LABEL_TRASH,
1274
+ inboxToken,
1275
+ );
1276
+ } else {
1277
+ console.error("Recieved an incorrect object");
1278
+ console.error(validationError);
1279
+ this.inboxes.label(
1280
+ inboxEndpoint,
1281
+ messageId,
1282
+ MESSAGE_LABEL_INVALID,
1283
+ inboxToken,
1284
+ );
1195
1285
  }
1196
1286
  }
1197
1287
  }
@@ -1,11 +1,11 @@
1
1
  export const template = `<template id="graffiti-login-welcome">
2
2
  <h1>
3
- <a target="_blank" href="https://graffiti.garden">Graffiti<wbr> Log In</a>
3
+ <a target="_blank" href="https://graffiti.garden">Graffiti Log&nbsp;In</a>
4
4
  </h1>
5
5
 
6
6
  <ul>
7
- <li><a type="button" id="graffiti-login-new">Create new Graffiti identity</a></li>
8
- <li><button class="secondary" id="graffiti-login-existing">Use existing Graffiti identity</button></li>
7
+ <li><a type="button" id="graffiti-login-new">Create&nbsp;new Graffiti&nbsp;identity</a></li>
8
+ <li><button class="secondary" id="graffiti-login-existing">Use&nbsp;existing Graffiti&nbsp;identity</button></li>
9
9
  </ul>
10
10
 
11
11
  <aside>
@@ -15,12 +15,12 @@ export const template = `<template id="graffiti-login-welcome">
15
15
  </template>
16
16
 
17
17
  <template id="graffiti-login-handle">
18
- <h1>
19
- <a target="_blank" href="https://graffiti.garden">Graffiti<wbr> Log In</a>
20
- </h1>
18
+ <h1>
19
+ <a target="_blank" href="https://graffiti.garden">Graffiti Log&nbsp;In</a>
20
+ </h1>
21
21
 
22
22
  <form id="graffiti-login-handle-form">
23
- <label for="username">Enter your Graffiti handle:</label>
23
+ <label for="username">Graffiti handle:</label>
24
24
  <input
25
25
  type="text"
26
26
  name="username"
@@ -29,7 +29,7 @@ export const template = `<template id="graffiti-login-welcome">
29
29
  autocapitalize="none"
30
30
  spellcheck="false"
31
31
  inputmode="url"
32
- placeholder="example.graffiti.actor"
32
+ placeholder="you.graffiti.actor"
33
33
  required
34
34
  >
35
35
  <button id="graffiti-login-handle-submit" type="submit">
@@ -38,6 +38,6 @@ export const template = `<template id="graffiti-login-welcome">
38
38
  </form>
39
39
 
40
40
  <p>
41
- Don't have a Graffiti handle? <a id="graffiti-login-new">Create one</a>.
41
+ Don't&nbsp;have&nbsp;a Graffiti&nbsp;handle? <a id="graffiti-login-new">Create&nbsp;one</a>.
42
42
  </p>
43
43
  </template>`;
@@ -1,44 +0,0 @@
1
- import"./chunk-RFBBAUMM.js";var t=`<template id="graffiti-login-welcome">
2
- <h1>
3
- <a target="_blank" href="https://graffiti.garden">Graffiti<wbr> Log In</a>
4
- </h1>
5
-
6
- <ul>
7
- <li><a type="button" id="graffiti-login-new">Create new Graffiti identity</a></li>
8
- <li><button class="secondary" id="graffiti-login-existing">Use existing Graffiti identity</button></li>
9
- </ul>
10
-
11
- <aside>
12
- This application is built with
13
- <a target="_blank" href="https://graffiti.garden">Graffiti</a>.
14
- </aside>
15
- </template>
16
-
17
- <template id="graffiti-login-handle">
18
- <h1>
19
- <a target="_blank" href="https://graffiti.garden">Graffiti<wbr> Log In</a>
20
- </h1>
21
-
22
- <form id="graffiti-login-handle-form">
23
- <label for="username">Enter your Graffiti handle:</label>
24
- <input
25
- type="text"
26
- name="username"
27
- id="username"
28
- autocomplete="username"
29
- autocapitalize="none"
30
- spellcheck="false"
31
- inputmode="url"
32
- placeholder="example.graffiti.actor"
33
- required
34
- >
35
- <button id="graffiti-login-handle-submit" type="submit">
36
- Log In
37
- </button>
38
- </form>
39
-
40
- <p>
41
- Don't have a Graffiti handle? <a id="graffiti-login-new">Create one</a>.
42
- </p>
43
- </template>`;export{t as template};
44
- //# sourceMappingURL=login-dialog.html-XUWYDNNI.js.map
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../src/3-protocol/login-dialog.html.ts"],
4
- "sourcesContent": ["export const template = `<template id=\"graffiti-login-welcome\">\n <h1>\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti<wbr> Log In</a>\n </h1>\n\n <ul>\n <li><a type=\"button\" id=\"graffiti-login-new\">Create new Graffiti identity</a></li>\n <li><button class=\"secondary\" id=\"graffiti-login-existing\">Use existing Graffiti identity</button></li>\n </ul>\n\n <aside>\n This application is built with\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti</a>.\n </aside>\n</template>\n\n<template id=\"graffiti-login-handle\">\n <h1>\n <a target=\"_blank\" href=\"https://graffiti.garden\">Graffiti<wbr> Log In</a>\n </h1>\n\n <form id=\"graffiti-login-handle-form\">\n <label for=\"username\">Enter your Graffiti handle:</label>\n <input\n type=\"text\"\n name=\"username\"\n id=\"username\"\n autocomplete=\"username\"\n autocapitalize=\"none\"\n spellcheck=\"false\"\n inputmode=\"url\"\n placeholder=\"example.graffiti.actor\"\n required\n >\n <button id=\"graffiti-login-handle-submit\" type=\"submit\">\n Log In\n </button>\n </form>\n\n <p>\n Don't have a Graffiti handle? <a id=\"graffiti-login-new\">Create one</a>.\n </p>\n</template>`;\n"],
5
- "mappings": "4BAAO,IAAMA,EAAW;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;",
6
- "names": ["template"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../node_modules/@graffiti-garden/modal/src/style.css"],
4
- "sourcesContent": [".graffiti-modal {\n --back: rgb(26, 26, 26, 0.85);\n --halfback: rgba(80, 80, 80, 0.85);\n --halfback2: rgba(26, 26, 26, 0.85);\n --hover: rgba(202, 122, 204, 0.3);\n --frontfaded: rgba(190, 190, 190);\n --front: rgba(240, 240, 240);\n --emph: rgb(202, 122, 204);\n --blurpix: 3px;\n border-color: var(--emph);\n box-sizing: border-box;\n border-width: 2px;\n padding: 0;\n margin: 0;\n border-radius: 1rem;\n box-shadow: 0 0 2rem black;\n overflow: hidden;\n opacity: 0;\n transition: opacity 0.3s;\n pointer-events: none;\n display: block;\n min-width: 95dvw;\n min-height: 95dvh;\n height: 95dvh;\n top: 50%;\n left: 50%;\n transform: translate(-50%, -50%);\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: center;\n font-family:\n Inter,\n -apple-system,\n BlinkMacSystemFont,\n \"Segoe UI\",\n Roboto,\n Oxygen,\n Ubuntu,\n Cantarell,\n \"Fira Sans\",\n \"Droid Sans\",\n \"Helvetica Neue\",\n sans-serif;\n color: var(--front);\n font-size: 150%;\n\n * {\n box-sizing: border-box;\n padding: 0;\n margin: 0;\n }\n\n ::selection {\n background: rgba(202, 122, 204, 0.3);\n }\n\n :focus {\n outline: 2px solid var(--front);\n }\n\n header {\n width: 100%;\n display: flex;\n justify-content: flex-end;\n }\n\n main {\n flex: 1;\n max-width: 600px;\n width: 100%;\n gap: 2em;\n padding-top: 4dvh;\n padding-bottom: 4dvh;\n margin-top: 4dvh;\n margin-bottom: 4dvh;\n margin-left: 4dvw;\n margin-right: 4dvw;\n padding-left: 4dvw;\n padding-right: 4dvw;\n background: var(--back);\n border-radius: 1rem;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n scrollbar-color: var(--emph) rgba(0, 0, 0, 0);\n }\n\n ul {\n list-style-type: none;\n display: flex;\n flex-direction: column;\n gap: 0.5em;\n align-items: stretch;\n justify-content: stretch;\n }\n\n aside {\n color: var(--frontfaded);\n }\n\n .secondary,\n a:not([type=\"button\"]) {\n color: var(--emph);\n }\n\n h1 {\n font-size: 120%;\n font-family:\n Rock Salt,\n cursive,\n sans-serif;\n letter-spacing: 0.1em;\n text-align: center;\n color: var(--front);\n }\n\n h1 a:not([type=\"button\"]) {\n color: inherit;\n }\n\n h1 a:hover {\n background: none;\n color: inherit;\n }\n\n button,\n input[type=\"submit\"],\n input[type=\"text\"],\n a[type=\"button\"] {\n font-size: inherit;\n width: 100%;\n text-align: center;\n display: block;\n border-radius: 1rem;\n border: 2px solid var(--emph);\n padding: 1em;\n padding-top: 0.5em;\n padding-bottom: 0.5em;\n transition: 0.1s;\n text-overflow: ellipsis;\n background: none;\n line-height: 1.2em;\n }\n\n input[type=\"text\"] {\n font-weight: 500;\n background: var(--front);\n text-align: left;\n color: black;\n }\n\n header button {\n border-radius: 0 0 0 1rem;\n border-right: none;\n border-top: none;\n background-color: var(--halfback2);\n width: fit-content;\n position: relative;\n overflow: hidden;\n }\n\n header button::before {\n z-index: -1;\n top: 0;\n left: 0;\n height: 100%;\n position: absolute;\n width: 100%;\n content: \"\";\n background-color: var(--back);\n }\n\n @media (max-width: 600px) {\n main {\n border-radius: 0;\n margin: auto;\n overflow: auto;\n }\n\n header button {\n border-radius: 0;\n border-left: none;\n width: 100%;\n }\n\n html {\n justify-content: safe center;\n }\n }\n\n a {\n text-decoration: none;\n }\n\n :is(button, ul a, input[type=\"submit\"]):hover {\n cursor: pointer;\n background: var(--hover);\n color: var(--front);\n }\n\n a:hover {\n text-decoration: underline;\n cursor: pointer;\n }\n\n form {\n display: flex;\n flex-direction: column;\n justify-content: flex-start;\n align-items: stretch;\n gap: 0.5em;\n }\n\n iframe {\n width: 100%;\n height: 100%;\n border: none;\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]).secondary {\n color: rgb(244, 213, 244);\n background: rgba(26, 26, 26, 0.6);\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]):not(.secondary) {\n background: var(--hover);\n color: white;\n }\n\n :is(button, a[type=\"button\"], input[type=\"submit\"]):hover {\n background: rgba(202, 122, 204, 0.6);\n color: white;\n text-decoration: none;\n }\n\n h3 {\n color: var(--frontfaded);\n }\n\n h2 {\n text-align: center;\n font-weight: 500;\n font-size: 150%;\n background: var(--halfback2);\n padding: 1rem;\n border-radius: 1rem;\n border: 1px solid var(--frontfaded);\n color: var(--front);\n }\n\n main > div {\n display: flex;\n flex-direction: column;\n justify-content: center;\n align-items: stretch;\n gap: 0.5rem;\n }\n}\n\n.graffiti-modal[open] {\n pointer-events: inherit;\n opacity: 1;\n}\n\n.graffiti-modal::backdrop {\n background-color: black;\n opacity: 0.5;\n}\n\n.graffiti-modal::before {\n content: \"\";\n position: fixed;\n left: 0;\n right: 0;\n z-index: -1;\n background-image: url(graffiti.jpg);\n background-size: cover;\n background-repeat: no-repeat;\n background-position: 50% 50%;\n height: calc(100% + 2 * var(--blurpix));\n width: calc(100% + 2 * var(--blurpix));\n filter: blur(var(--blurpix));\n margin: calc(-1 * var(--blurpix));\n}\n"],
5
- "mappings": "4BAAA,IAAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;",
6
- "names": ["style_default"]
7
- }