@lokascript/compilation-service 3.1.1 → 3.3.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.
@@ -1964,6 +1964,39 @@ function makeTypeCheckHandler(negated) {
1964
1964
  };
1965
1965
  };
1966
1966
  }
1967
+ var OF_OPERAND_BP = 70;
1968
+ function parseOfOperand(ctx, minBp = OF_OPERAND_BP) {
1969
+ let right = ctx.parseExpr(minBp);
1970
+ if (ctx.peek()?.value === "in") {
1971
+ const inToken = ctx.advance();
1972
+ const scope = ctx.parseExpr(minBp);
1973
+ right = {
1974
+ type: "binaryExpression",
1975
+ operator: "in",
1976
+ left: right,
1977
+ right: scope,
1978
+ start: right.start,
1979
+ end: scope.end ?? inToken.end,
1980
+ line: right.line ?? inToken.line,
1981
+ column: right.column ?? inToken.column
1982
+ };
1983
+ }
1984
+ return right;
1985
+ }
1986
+ var THE_OF_TARGET_BP = OF_OPERAND_BP + 1;
1987
+ function ofHandler(left, token, ctx) {
1988
+ const right = parseOfOperand(ctx);
1989
+ return {
1990
+ type: "binaryExpression",
1991
+ operator: token.value,
1992
+ left,
1993
+ right,
1994
+ start: left.start,
1995
+ end: right.end ?? token.end,
1996
+ line: left.line ?? token.line,
1997
+ column: left.column ?? token.column
1998
+ };
1999
+ }
1967
2000
  var PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
1968
2001
  ["===", leftAssoc(30)],
1969
2002
  ["!==", leftAssoc(30)],
@@ -2008,7 +2041,7 @@ var PARSER_COMPARISON_FRAGMENT = /* @__PURE__ */ new Map([
2008
2041
  ["is greater than or equal to", leftAssoc(30)],
2009
2042
  ["is less than or equal to", leftAssoc(30)],
2010
2043
  ["in", leftAssoc(30)],
2011
- ["of", rightAssoc(30)],
2044
+ ["of", { infix: { bp: [90, OF_OPERAND_BP], handler: ofHandler } }],
2012
2045
  ["really", leftAssoc(30)],
2013
2046
  [
2014
2047
  "exists",
@@ -2694,6 +2727,48 @@ function parseMaybeNamedArgument(ctx) {
2694
2727
  const value = ctx.parseExpression();
2695
2728
  return name !== void 0 ? { name, value } : { value };
2696
2729
  }
2730
+ function isNakedNamedArgStart(ctx) {
2731
+ if (ctx.check("{"))
2732
+ return false;
2733
+ if (!ctx.checkIdentifierLike())
2734
+ return false;
2735
+ const next = ctx.peekAt(1);
2736
+ return next !== null && next.value === ":";
2737
+ }
2738
+ function parseNakedNamedArgs(ctx, parseValue, command2) {
2739
+ const properties = [];
2740
+ const startPos = ctx.getPosition();
2741
+ do {
2742
+ if (!ctx.checkIdentifierLike())
2743
+ break;
2744
+ const keyToken = ctx.advance();
2745
+ const key = {
2746
+ type: "identifier",
2747
+ name: keyToken.value,
2748
+ start: keyToken.start,
2749
+ end: keyToken.end,
2750
+ line: keyToken.line,
2751
+ column: keyToken.column
2752
+ };
2753
+ ctx.consume(":", `Expected ':' after property name in ${command2} named arguments`);
2754
+ const value = parseValue();
2755
+ if (value) {
2756
+ properties.push({ key, value });
2757
+ }
2758
+ } while (ctx.match(",") && !ctx.isAtEnd());
2759
+ const endPos = ctx.getPosition();
2760
+ return {
2761
+ type: "objectLiteral",
2762
+ properties,
2763
+ start: startPos.start,
2764
+ end: endPos.end,
2765
+ line: startPos.line,
2766
+ column: startPos.column
2767
+ };
2768
+ }
2769
+ function isGluedBeepBang(beep, bang) {
2770
+ return beep?.value === "beep" && bang?.value === "!" && bang.start === beep.end;
2771
+ }
2697
2772
  var CommandNodeBuilder = class _CommandNodeBuilder {
2698
2773
  constructor(name) {
2699
2774
  this.args = [];
@@ -2890,6 +2965,8 @@ function parseTriggerCommand(ctx, identifierNode) {
2890
2965
  const modifiers = {};
2891
2966
  while (!isCommandBoundary(ctx)) {
2892
2967
  if (ctx.check(KEYWORDS.ON) || ctx.check(KEYWORDS.TO)) {
2968
+ if (modifiers["on"])
2969
+ break;
2893
2970
  ctx.advance();
2894
2971
  const target = parseOneArgument(ctx, [KEYWORDS.WITH]);
2895
2972
  if (target)
@@ -3021,6 +3098,8 @@ function parseRepeatCommand(ctx, commandToken) {
3021
3098
  } else if (ctx.check(KEYWORDS.FOREVER)) {
3022
3099
  ctx.advance();
3023
3100
  loopType = KEYWORDS.FOREVER;
3101
+ } else if (ctx.checkIsCommand() || ctx.isCommand(ctx.peek().value)) {
3102
+ loopType = KEYWORDS.FOREVER;
3024
3103
  } else {
3025
3104
  times = ctx.parseExpression();
3026
3105
  if (ctx.check(KEYWORDS.TIMES)) {
@@ -3060,10 +3139,11 @@ function parseRepeatCommand(ctx, commandToken) {
3060
3139
  loopType = result.terminator;
3061
3140
  ctx.advance();
3062
3141
  condition = ctx.parseExpression();
3063
- if (!ctx.check("end")) {
3142
+ if (ctx.check("end")) {
3143
+ ctx.advance();
3144
+ } else if (!ctx.atEndOfInput()) {
3064
3145
  throw new Error('Expected "end" to close repeat block');
3065
3146
  }
3066
- ctx.advance();
3067
3147
  }
3068
3148
  } else {
3069
3149
  const result = ctx.parseCommandListUntilEndOrElse();
@@ -3292,7 +3372,7 @@ function parseIfCommand(ctx, commandToken) {
3292
3372
  }));
3293
3373
  }
3294
3374
  }
3295
- if (!consumedElseIf && !ctx.isAtEnd()) {
3375
+ if (!consumedElseIf && !ctx.atEndOfInput()) {
3296
3376
  ctx.consume(KEYWORDS.END, "Expected 'end' after if block");
3297
3377
  }
3298
3378
  } else {
@@ -3312,7 +3392,6 @@ function parseIfCommand(ctx, commandToken) {
3312
3392
  return CommandNodeBuilder.from(commandToken).withArgs(...args).endingAt(ctx.getPosition()).build();
3313
3393
  }
3314
3394
  function parseForCommand(ctx, commandToken) {
3315
- const args = [];
3316
3395
  let variable = null;
3317
3396
  let collection = null;
3318
3397
  if (ctx.check(KEYWORDS.EACH)) {
@@ -3352,30 +3431,20 @@ function parseForCommand(ctx, commandToken) {
3352
3431
  }
3353
3432
  }
3354
3433
  const commands = ctx.parseCommandListUntilEnd("for");
3355
- args.push({
3356
- type: "identifier",
3357
- name: "for",
3358
- start: commandToken.start,
3359
- end: commandToken.end,
3360
- line: commandToken.line,
3361
- column: commandToken.column
3362
- });
3363
- const forPos = {
3434
+ const pos2 = {
3364
3435
  start: commandToken.start,
3365
3436
  end: commandToken.end,
3366
3437
  line: commandToken.line,
3367
3438
  column: commandToken.column
3368
3439
  };
3369
- args.push(createStringLiteral(variable, forPos));
3370
- args.push(collection);
3371
- if (indexVariable) {
3372
- args.push(createStringLiteral(indexVariable, forPos));
3373
- }
3374
- args.push(createBlock(commands, { ...forPos, end: forPos.end || 0 }));
3375
- return CommandNodeBuilder.from({
3376
- ...commandToken,
3377
- value: "repeat"
3378
- }).withArgs(...args).endingAt(ctx.getPosition()).build();
3440
+ const modifiers = {
3441
+ loopType: toLegacyExpression(createStringLiteral(KEYWORDS.FOR, pos2)),
3442
+ for: toLegacyExpression(createStringLiteral(variable, pos2)),
3443
+ in: collection
3444
+ };
3445
+ if (indexVariable)
3446
+ modifiers["index"] = toLegacyExpression(createStringLiteral(indexVariable, pos2));
3447
+ return CommandNodeBuilder.from({ ...commandToken, value: "repeat" }).withArgs(createBlock(commands, { ...pos2, end: pos2.end || 0 })).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3379
3448
  }
3380
3449
  function parseMeasureCommand(ctx, identifierNode) {
3381
3450
  const args = [];
@@ -3424,75 +3493,144 @@ function parseMeasureCommand(ctx, identifierNode) {
3424
3493
  }
3425
3494
  return builder.build();
3426
3495
  }
3427
- var CONTEXT_POSSESSIVES = /* @__PURE__ */ new Set(["my", "its", "your"]);
3496
+ var CONTEXT_REFS = /* @__PURE__ */ new Set(["me", "it", "you", "result"]);
3428
3497
  function parseTransitionCommand(ctx, commandToken) {
3429
- const args = [];
3430
- const modifiers = {};
3431
- let property = null;
3432
- let target = null;
3433
- const leadToken = ctx.peek();
3434
- const startsTarget = ctx.checkAnySelector() || ctx.checkContextVar() || ctx.check("<") || isIdentifierLike(leadToken) && CONTEXT_POSSESSIVES.has(leadToken.value);
3435
- if (startsTarget) {
3436
- const parsed = ctx.parsePrimary();
3437
- if ((parsed.type === "possessiveExpression" || parsed.type === "memberExpression" && !parsed.computed) && parsed.object && parsed.property?.name) {
3438
- target = parsed.object;
3439
- property = {
3440
- type: "string",
3441
- value: parsed.property.name,
3442
- start: leadToken.start || 0,
3443
- end: ctx.getPosition().end,
3444
- line: leadToken.line,
3445
- column: leadToken.column
3446
- };
3447
- } else {
3448
- target = parsed;
3449
- if (ctx.check("'s"))
3450
- ctx.advance();
3451
- }
3452
- }
3453
- const firstToken = ctx.peek();
3454
- if (!property && (isIdentifierLike(firstToken) || firstToken.value === "*")) {
3455
- let propertyValue = "";
3456
- if (ctx.check("*")) {
3457
- propertyValue = "*";
3498
+ const pairs = [];
3499
+ do {
3500
+ pairs.push(parseTransitionPair(ctx));
3501
+ } while (startsAnotherPair(ctx));
3502
+ const [first, ...rest] = pairs;
3503
+ const args = first.owner ? [first.owner, first.property] : [first.property];
3504
+ const modifiers = { to: first.to };
3505
+ if (first.from)
3506
+ modifiers["from"] = first.from;
3507
+ if (rest.length > 0)
3508
+ modifiers["pairs"] = pairsNode(rest);
3509
+ for (let i = 0; i < 3 && !ctx.isAtEnd(); i++) {
3510
+ if (ctx.check("over") && !modifiers["over"]) {
3458
3511
  ctx.advance();
3459
- }
3460
- const hyphenatedName = parseHyphenatedName(ctx);
3461
- if (hyphenatedName) {
3462
- propertyValue += hyphenatedName;
3463
- property = {
3464
- type: "string",
3465
- value: propertyValue,
3466
- start: firstToken.start || 0,
3467
- end: ctx.getPosition().end,
3468
- line: firstToken.line,
3469
- column: firstToken.column
3470
- };
3512
+ modifiers["over"] = ctx.parseExpression();
3513
+ } else if (ctx.check("using") && !modifiers["using"]) {
3514
+ ctx.advance();
3515
+ modifiers["using"] = ctx.parseExpression();
3516
+ } else if (ctx.check(KEYWORDS.WITH) && !modifiers["with"]) {
3517
+ ctx.advance();
3518
+ modifiers["with"] = ctx.parsePrimary();
3519
+ } else {
3520
+ break;
3471
3521
  }
3472
3522
  }
3473
- if (!property) {
3474
- throw new Error("Transition command requires a CSS property");
3523
+ return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3524
+ }
3525
+ function parseTransitionPair(ctx) {
3526
+ const { owner, property } = parseTransitionProperty(ctx);
3527
+ let from = null;
3528
+ if (ctx.check("from")) {
3529
+ ctx.advance();
3530
+ from = ctx.parseExpression();
3475
3531
  }
3476
- if (target)
3477
- args.push(target);
3478
- args.push(property);
3479
3532
  if (!ctx.check(KEYWORDS.TO)) {
3480
3533
  throw new Error('Expected "to" keyword after property in transition command');
3481
3534
  }
3482
3535
  ctx.advance();
3483
- const value = ctx.parseExpression();
3484
- modifiers["to"] = value;
3485
- if (ctx.check("over")) {
3486
- ctx.advance();
3487
- const duration = ctx.parseExpression();
3488
- modifiers["over"] = duration;
3536
+ const to = ctx.parseExpression();
3537
+ return { owner, property, from, to };
3538
+ }
3539
+ function parseTransitionProperty(ctx) {
3540
+ const lead = ctx.peek();
3541
+ if (!lead || isKeyword(lead, [KEYWORDS.TO, "from"])) {
3542
+ throw new Error("Transition command requires a CSS property");
3489
3543
  }
3490
- if (ctx.check(KEYWORDS.WITH)) {
3544
+ const expr = ctx.parseExpression();
3545
+ const split = splitStyleExpression(expr);
3546
+ if (split)
3547
+ return { owner: split.owner, property: propertyNode(split.name, lead, ctx) };
3548
+ let name = "";
3549
+ if (ctx.check("*")) {
3550
+ name = "*";
3491
3551
  ctx.advance();
3492
- const timingFunction = ctx.parsePrimary();
3493
- modifiers["with"] = timingFunction;
3494
3552
  }
3495
- return CommandNodeBuilder.from(commandToken).withArgs(...args).withModifiers(modifiers).endingAt(ctx.getPosition()).build();
3553
+ const hyphenated = parseHyphenatedName(ctx);
3554
+ if (!hyphenated)
3555
+ throw new Error("Transition command requires a CSS property");
3556
+ return { owner: expr, property: propertyNode(name + hyphenated, lead, ctx) };
3557
+ }
3558
+ function splitStyleExpression(node) {
3559
+ const n = node;
3560
+ switch (n.type) {
3561
+ case "selector":
3562
+ return typeof n.value === "string" && n.value.startsWith("*") ? { owner: null, name: n.value } : null;
3563
+ case "identifier":
3564
+ return typeof n.name === "string" && !CONTEXT_REFS.has(n.name) ? { owner: null, name: n.name } : null;
3565
+ case "possessiveExpression":
3566
+ case "memberExpression":
3567
+ return !n.computed && n.object && typeof n.property?.name === "string" ? { owner: n.object, name: n.property.name } : null;
3568
+ case "binaryExpression": {
3569
+ if (n.operator !== "of" || !n.left || !n.right)
3570
+ return null;
3571
+ const inner = splitStyleExpression(n.left);
3572
+ return inner && !inner.owner ? { owner: n.right, name: inner.name } : null;
3573
+ }
3574
+ case "propertyOfExpression":
3575
+ return n.target && typeof n.property?.name === "string" ? { owner: n.target, name: n.property.name } : null;
3576
+ default:
3577
+ return null;
3578
+ }
3579
+ }
3580
+ function propertyNode(name, lead, ctx) {
3581
+ return {
3582
+ type: "string",
3583
+ value: name,
3584
+ start: lead.start || 0,
3585
+ end: ctx.getPosition().end,
3586
+ line: lead.line,
3587
+ column: lead.column
3588
+ };
3589
+ }
3590
+ function startsAnotherPair(ctx) {
3591
+ if (ctx.isAtEnd() || isComment(ctx.peek()))
3592
+ return false;
3593
+ if (isKeyword(ctx.peek(), ["over", "using", KEYWORDS.WITH]))
3594
+ return false;
3595
+ return !isCommandBoundary(ctx, ["catch", "finally", "on"]);
3596
+ }
3597
+ function pairsNode(pairs) {
3598
+ const field = (name, value) => ({
3599
+ key: {
3600
+ type: "identifier",
3601
+ name,
3602
+ start: value.start,
3603
+ end: value.end,
3604
+ line: value.line,
3605
+ column: value.column
3606
+ },
3607
+ value
3608
+ });
3609
+ const elements = pairs.map((pair) => {
3610
+ const properties = [field("property", pair.property), field("to", pair.to)];
3611
+ if (pair.owner)
3612
+ properties.push(field("owner", pair.owner));
3613
+ if (pair.from)
3614
+ properties.push(field("from", pair.from));
3615
+ return {
3616
+ type: "objectLiteral",
3617
+ properties,
3618
+ start: (pair.owner ?? pair.property).start,
3619
+ end: pair.to.end,
3620
+ line: (pair.owner ?? pair.property).line,
3621
+ column: (pair.owner ?? pair.property).column
3622
+ };
3623
+ });
3624
+ const firstEl = elements[0];
3625
+ const lastEl = elements[elements.length - 1];
3626
+ return {
3627
+ type: "arrayLiteral",
3628
+ elements,
3629
+ start: firstEl.start,
3630
+ end: lastEl.end,
3631
+ line: firstEl.line,
3632
+ column: firstEl.column
3633
+ };
3496
3634
  }
3497
3635
  function parseStartCommand(ctx, identifierNode) {
3498
3636
  if (!ctx.match("view")) {
@@ -3598,6 +3736,9 @@ function parseToggleCommand(ctx, identifierNode) {
3598
3736
  if (classArg && mode !== void 0 && classArg.expression) {
3599
3737
  args.push(classArg.expression);
3600
3738
  modifiers["as"] = literalModifier(mode);
3739
+ } else if (classArg?.type === "binaryExpression" && classArg.operator === "of" && classArg.left?.type === "selector" && typeof classArg.left.value === "string" && classArg.left.value.startsWith("*") && classArg.right) {
3740
+ args.push(classArg.left);
3741
+ modifiers["on"] = classArg.right;
3601
3742
  } else if (classArg) {
3602
3743
  args.push(classArg);
3603
3744
  }
@@ -3754,11 +3895,15 @@ function parseSwapCommand(ctx, identifierNode) {
3754
3895
  const targetExpr = isDelete ? ctx.parseExpression() : parseOneArgument(ctx, [KEYWORDS.WITH]);
3755
3896
  if (targetExpr)
3756
3897
  args.push(targetExpr);
3757
- if (!isDelete && consumeOptionalKeyword(ctx, KEYWORDS.WITH)) {
3898
+ const isMorph = identifierNode.name === "morph";
3899
+ if (!isDelete && (consumeOptionalKeyword(ctx, KEYWORDS.WITH) || isMorph && consumeOptionalKeyword(ctx, KEYWORDS.TO))) {
3758
3900
  const contentExpr = parseOneArgument(ctx, ["using"]);
3759
3901
  if (contentExpr)
3760
3902
  modifiers["with"] = contentExpr;
3761
3903
  }
3904
+ if (isMorph && modifiers["with"] === void 0) {
3905
+ ctx.addError("Expected 'to' and the content to morph into");
3906
+ }
3762
3907
  return CommandNodeBuilder.fromIdentifier(identifierNode).withArgs(...args).withModifiers({ ...modifiers, ...parseViewTransitionTail(ctx) }).endingAt(ctx.getPosition()).build();
3763
3908
  }
3764
3909
  function parseShowHideCommand(ctx, identifierNode) {
@@ -4448,6 +4593,8 @@ function atArgumentBoundary(ctx, grammar) {
4448
4593
  const next = ctx.peek();
4449
4594
  if (ctx.checkIsCommand() && ctx.peekAt(1)?.value === "(")
4450
4595
  return false;
4596
+ if (isGluedBeepBang(next, ctx.peekAt(1)))
4597
+ return false;
4451
4598
  if (isCommandBoundary(ctx, ["catch", "finally", "on"]))
4452
4599
  return true;
4453
4600
  return isKeyword(next, [...grammar.markers]);
@@ -4479,10 +4626,11 @@ function parseDeclaredCommand(ctx, commandToken, name, grammar) {
4479
4626
  }
4480
4627
  }
4481
4628
  const commaList = new Set(grammar.commaList ?? []);
4629
+ const namedArgs = new Set(grammar.namedArgs ?? []);
4482
4630
  while (!ctx.isAtEnd() && isKeyword(ctx.peek(), [...grammar.markers])) {
4483
4631
  const keyword = ctx.advance().value;
4484
4632
  const key = ctx.resolveKeyword(keyword).toLowerCase();
4485
- let value = ctx.parseExpression();
4633
+ let value = namedArgs.has(key) && isNakedNamedArgStart(ctx) ? parseNakedNamedArgs(ctx, () => ctx.parseExpression(), name) : ctx.parseExpression();
4486
4634
  if (value && commaList.has(key) && ctx.check(",")) {
4487
4635
  const elements = [value];
4488
4636
  while (ctx.match(",")) {
@@ -4645,8 +4793,8 @@ function parseFetchCommand(ctx, commandToken) {
4645
4793
  }
4646
4794
  if (ctx.check("with") && !modifiers["with"]) {
4647
4795
  ctx.advance();
4648
- if (isFetchNakedNamedArgStart(ctx)) {
4649
- modifiers["with"] = parseFetchNakedNamedArgs(ctx);
4796
+ if (isNakedNamedArgStart(ctx)) {
4797
+ modifiers["with"] = parseNakedNamedArgs(ctx, () => ctx.parsePrimary(), "fetch");
4650
4798
  } else {
4651
4799
  modifiers["with"] = ctx.parsePrimary();
4652
4800
  }
@@ -4678,45 +4826,6 @@ function parseFetchCommand(ctx, commandToken) {
4678
4826
  }
4679
4827
  return builder.build();
4680
4828
  }
4681
- function isFetchNakedNamedArgStart(ctx) {
4682
- if (ctx.check("{"))
4683
- return false;
4684
- if (!ctx.checkIdentifierLike())
4685
- return false;
4686
- const next = ctx.peekAt(1);
4687
- return next !== null && next.value === ":";
4688
- }
4689
- function parseFetchNakedNamedArgs(ctx) {
4690
- const properties = [];
4691
- const startPos = ctx.getPosition();
4692
- do {
4693
- if (!ctx.checkIdentifierLike())
4694
- break;
4695
- const keyToken = ctx.advance();
4696
- const key = {
4697
- type: "identifier",
4698
- name: keyToken.value,
4699
- start: keyToken.start,
4700
- end: keyToken.end,
4701
- line: keyToken.line,
4702
- column: keyToken.column
4703
- };
4704
- ctx.consume(":", "Expected ':' after property name in fetch named arguments");
4705
- const value = ctx.parsePrimary();
4706
- if (value) {
4707
- properties.push({ key, value });
4708
- }
4709
- } while (ctx.match(",") && !ctx.isAtEnd());
4710
- const endPos = ctx.getPosition();
4711
- return {
4712
- type: "objectLiteral",
4713
- properties,
4714
- start: startPos.start,
4715
- end: endPos.end,
4716
- line: startPos.line,
4717
- column: startPos.column
4718
- };
4719
- }
4720
4829
  function findJsEndBoundary(ctx, startPos) {
4721
4830
  const input = ctx.getInputSlice(startPos);
4722
4831
  if (!input) {
@@ -4830,6 +4939,7 @@ function parseTellCommand(ctx, identifierNode) {
4830
4939
  const commands = [];
4831
4940
  while (!ctx.isAtEnd()) {
4832
4941
  if (ctx.checkIsCommand()) {
4942
+ const word = ctx.peek().value;
4833
4943
  try {
4834
4944
  ctx.advance();
4835
4945
  const cmd = ctx.parseCommand();
@@ -4838,7 +4948,8 @@ function parseTellCommand(ctx, identifierNode) {
4838
4948
  } else {
4839
4949
  break;
4840
4950
  }
4841
- } catch {
4951
+ } catch (error) {
4952
+ ctx.addError(`'${word}' in a tell body failed to parse: ${error instanceof Error ? error.message : String(error)}`);
4842
4953
  break;
4843
4954
  }
4844
4955
  if (ctx.match(KEYWORDS.AND) || ctx.match(KEYWORDS.THEN)) {
@@ -4959,7 +5070,6 @@ var ONE_EXPR = (syntax) => ({
4959
5070
  markers: [],
4960
5071
  syntax
4961
5072
  });
4962
- var BEEP = ONE_EXPR("beep! [<expression>, \u2026]");
4963
5073
  var COMMAND_GRAMMAR = {
4964
5074
  append: { positional: "expression", markers: ["to"], syntax: "append <value> [to <target>]" },
4965
5075
  prepend: { positional: "expression", markers: ["to"], syntax: "prepend <value> [to <target>]" },
@@ -4970,8 +5080,7 @@ var COMMAND_GRAMMAR = {
4970
5080
  syntax: "make (a|an) <type> [from <args>] [called <name>]"
4971
5081
  },
4972
5082
  throw: { positional: "primary", markers: [], syntax: "throw <error>" },
4973
- "beep!": BEEP,
4974
- beep: BEEP,
5083
+ beep: ONE_EXPR("beep! [<expression>, \u2026]"),
4975
5084
  blur: ONE_EXPR("blur [<target>]"),
4976
5085
  break: NONE,
4977
5086
  breakpoint: NONE,
@@ -4995,7 +5104,8 @@ var COMMAND_GRAMMAR = {
4995
5104
  render: {
4996
5105
  positional: "expression",
4997
5106
  markers: ["with"],
4998
- syntax: "render <template> [with <variables>]"
5107
+ namedArgs: ["with"],
5108
+ syntax: "render <template> [with <name>: <value>, \u2026]"
4999
5109
  },
5000
5110
  reset: ONE_EXPR("reset [<form>]"),
5001
5111
  return: ONE_EXPR("return [<value>]"),
@@ -5334,7 +5444,9 @@ var Parser = class _Parser {
5334
5444
  const firstToken = this.peek();
5335
5445
  const prefixEntry = _Parser.PRATT_TABLE.get(firstToken.value);
5336
5446
  let left;
5337
- if (prefixEntry?.prefix) {
5447
+ if (isGluedBeepBang(firstToken, this.tokens[this.current + 1])) {
5448
+ left = this.parseBeepExpression();
5449
+ } else if (prefixEntry?.prefix) {
5338
5450
  const token = this.advance();
5339
5451
  if (this.isAtEnd()) {
5340
5452
  this.addError(`Expected expression after '${token.value}' operator`);
@@ -5353,6 +5465,9 @@ var Parser = class _Parser {
5353
5465
  continue;
5354
5466
  }
5355
5467
  }
5468
+ if (nextToken.value === "*" && this.isSpacedStyleRef(this.current)) {
5469
+ break;
5470
+ }
5356
5471
  const infixEntry = _Parser.PRATT_TABLE.get(nextToken.value);
5357
5472
  if (!infixEntry?.infix) {
5358
5473
  if (STOP_TOKENS.has(nextToken.value) || STOP_DELIMITERS.has(nextToken.value)) {
@@ -5406,6 +5521,35 @@ var Parser = class _Parser {
5406
5521
  this.prattCache.set(cacheKey, { node: left, endPos: this.current });
5407
5522
  return left;
5408
5523
  }
5524
+ parseBeepExpression() {
5525
+ const beepToken = this.advance();
5526
+ this.advance();
5527
+ if (this.isAtEnd()) {
5528
+ this.addError("Expected expression after 'beep!'");
5529
+ return this.createErrorNode();
5530
+ }
5531
+ const operand = this.parseExpressionPratt(_Parser.BEEP_OPERAND_BP);
5532
+ return {
5533
+ type: "unaryExpression",
5534
+ operator: "beep!",
5535
+ operand,
5536
+ argument: operand,
5537
+ prefix: true,
5538
+ start: beepToken.start,
5539
+ end: this.previous().end,
5540
+ line: beepToken.line,
5541
+ column: beepToken.column
5542
+ };
5543
+ }
5544
+ isStyleRefStar(i) {
5545
+ const star = this.tokens[i];
5546
+ const name = this.tokens[i + 1];
5547
+ return star?.value === "*" && !!name && name.start === star.end && isIdentifierLike(name);
5548
+ }
5549
+ isSpacedStyleRef(i) {
5550
+ const prev = this.tokens[i - 1];
5551
+ return this.isStyleRefStar(i) && !!prev && prev.end < this.tokens[i].start;
5552
+ }
5409
5553
  tryParseStringPostfix(left, nextToken) {
5410
5554
  const v2 = nextToken.value;
5411
5555
  let unit = null;
@@ -5575,6 +5719,10 @@ var Parser = class _Parser {
5575
5719
  const isStop = () => this.check("end") || extraStops.some((s) => this.check(s));
5576
5720
  debug.parse("\u{1F504} parseCommandListUntilTerminator: Starting (extraStops:", extraStops.join(","), ")");
5577
5721
  while (!this.isAtEnd() && !isStop()) {
5722
+ if (this.checkComment()) {
5723
+ this.advance();
5724
+ continue;
5725
+ }
5578
5726
  debug.parse("\u{1F4CD} Loop iteration, current token:", this.peek().value, "kind:", this.peek().kind);
5579
5727
  let parsedCommand = false;
5580
5728
  const isCommandToken = this.checkIsCommand();
@@ -5646,7 +5794,7 @@ var Parser = class _Parser {
5646
5794
  if (this.check("end")) {
5647
5795
  debug.parse('\u2705 Found "end", consuming it');
5648
5796
  this.advance();
5649
- } else {
5797
+ } else if (!this.atEndOfInput()) {
5650
5798
  debug.parse('\u274C ERROR: Expected "end" but got:', this.peek().value, "at position:", this.peek().start);
5651
5799
  throw new Error(`Expected "end" to close ${construct} block`);
5652
5800
  }
@@ -5658,7 +5806,7 @@ var Parser = class _Parser {
5658
5806
  if (!hasElse) {
5659
5807
  if (this.check("end")) {
5660
5808
  this.advance();
5661
- } else {
5809
+ } else if (!this.atEndOfInput()) {
5662
5810
  throw new Error('Expected "end" to close repeat block');
5663
5811
  }
5664
5812
  }
@@ -5673,7 +5821,10 @@ var Parser = class _Parser {
5673
5821
  if (terminator === "end") {
5674
5822
  this.advance();
5675
5823
  } else if (terminator === "") {
5676
- throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
5824
+ if (!this.atEndOfInput()) {
5825
+ throw new Error('Expected "end", "else", "until", or "while" to close repeat block');
5826
+ }
5827
+ return { commands, terminator: "end" };
5677
5828
  }
5678
5829
  return { commands, terminator };
5679
5830
  }
@@ -6213,7 +6364,9 @@ var Parser = class _Parser {
6213
6364
  parseTopLevelInitBlock() {
6214
6365
  const pos2 = this.getPosition();
6215
6366
  const initCommands = this.parseCommandBlock(["end"]);
6216
- this.consume("end", "Expected 'end' after init block");
6367
+ if (!this.match("end") && !this.atEndOfInput() && !this.checkFeatureStart()) {
6368
+ this.consume("end", "Expected 'end' after init block");
6369
+ }
6217
6370
  return {
6218
6371
  type: "initBlock",
6219
6372
  commands: initCommands,
@@ -6382,10 +6535,27 @@ var Parser = class _Parser {
6382
6535
  ...sourceExpr.column !== void 0 && { column: sourceExpr.column }
6383
6536
  };
6384
6537
  }
6538
+ isArgumentListOnly(open) {
6539
+ let depth = 0;
6540
+ for (let i = open; i < this.tokens.length; i++) {
6541
+ const token = this.tokens[i];
6542
+ if (token.kind !== "operator")
6543
+ continue;
6544
+ if (token.value === "(" || token.value === "[" || token.value === "{")
6545
+ depth++;
6546
+ else if (token.value === ")" || token.value === "]" || token.value === "}") {
6547
+ if (--depth === 0)
6548
+ return i === open + 1;
6549
+ } else if (token.value === "," && depth === 1)
6550
+ return true;
6551
+ }
6552
+ return false;
6553
+ }
6385
6554
  tryParsePseudoCommand(callExpr, sourceExpr) {
6386
6555
  const methodName = callExpr.callee.name;
6387
6556
  const nextToken = this.peek();
6388
- const hasPseudoCommandPattern = _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
6557
+ const nextIsArticle = nextToken.value.toLowerCase() === "the";
6558
+ const hasPseudoCommandPattern = nextIsArticle || _Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(nextToken.value.toLowerCase()) || isIdentifier(nextToken) && !this.isCommand(nextToken.value) || isContextVar(nextToken);
6389
6559
  if (!hasPseudoCommandPattern) {
6390
6560
  return {
6391
6561
  node: this.callExprToCommandNode(callExpr, sourceExpr),
@@ -6409,7 +6579,9 @@ var Parser = class _Parser {
6409
6579
  });
6410
6580
  }
6411
6581
  let preposition;
6412
- if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
6582
+ if (nextIsArticle) {
6583
+ this.advance();
6584
+ } else if (_Parser.PSEUDO_COMMAND_PREPOSITIONS.includes(this.peek().value.toLowerCase())) {
6413
6585
  preposition = this.advance().value.toLowerCase();
6414
6586
  }
6415
6587
  let targetExpr;
@@ -6562,10 +6734,18 @@ var Parser = class _Parser {
6562
6734
  this.consume("]", "Expected ']' after event condition");
6563
6735
  }
6564
6736
  let target;
6737
+ let targetExpression;
6565
6738
  if (this.match("from")) {
6566
- const targetToken = this.advance();
6567
- target = targetToken.value;
6568
- debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target} (kind: ${targetToken.kind})`);
6739
+ if (this.check("the"))
6740
+ this.advance();
6741
+ if (_Parser.POSITIONAL_SOURCE_HEADS.has(this.peek().value.toLowerCase())) {
6742
+ const startIndex = this.current;
6743
+ targetExpression = this.parseExpression();
6744
+ target = this.tokens.slice(startIndex, this.current).map((t) => t.value).join(" ");
6745
+ } else {
6746
+ target = this.advance().value;
6747
+ }
6748
+ debug.parse(`\u{1F527} parseEventHandler: Parsed 'from' target: ${target}`);
6569
6749
  this.parseTemporalKeywordModifiers(modifiers);
6570
6750
  }
6571
6751
  let attributeName;
@@ -6610,7 +6790,7 @@ var Parser = class _Parser {
6610
6790
  const nextIsOpenParen = this.tokens[this.current + 1]?.value === "(";
6611
6791
  const commandName = this.peek().value.toLowerCase();
6612
6792
  const isSpecialBodyCommand = commandName === "js" || commandName === "tell";
6613
- if (nextIsOpenParen && !isSpecialBodyCommand) {
6793
+ if (nextIsOpenParen && !isSpecialBodyCommand && this.isArgumentListOnly(this.current + 1)) {
6614
6794
  let expr;
6615
6795
  const savedError = this.error;
6616
6796
  try {
@@ -6638,7 +6818,7 @@ var Parser = class _Parser {
6638
6818
  this.recordDropped(`Command '${cmdToken}' failed to parse and was discarded`);
6639
6819
  }
6640
6820
  }
6641
- } else if (this.checkIdentifier()) {
6821
+ } else if (this.checkIdentifier() || this.checkEventNamedCall()) {
6642
6822
  const token = this.peek();
6643
6823
  if (this.isCommand(token.value)) {
6644
6824
  const cmd = this.parseCommandWithErrorRecovery();
@@ -6724,6 +6904,7 @@ var Parser = class _Parser {
6724
6904
  ...eventParams.length > 0 && { args: eventParams },
6725
6905
  ...condition && { condition: fromLegacyExpression(condition) },
6726
6906
  ...target && { target },
6907
+ ...targetExpression && { targetExpression: fromLegacyExpression(targetExpression) },
6727
6908
  ...attributeName && { attributeName },
6728
6909
  ...watchTarget && { watchTarget: fromLegacyExpression(watchTarget) },
6729
6910
  ...customEventSource && { customEventSource },
@@ -6960,10 +7141,9 @@ var Parser = class _Parser {
6960
7141
  }
6961
7142
  parseCommandCore() {
6962
7143
  const commandToken = this.previous();
6963
- let commandName = commandToken.value;
7144
+ const commandName = commandToken.value;
6964
7145
  if (commandName === "beep" && this.check("!")) {
6965
7146
  this.advance();
6966
- commandName = "beep!";
6967
7147
  }
6968
7148
  if (commandName === "fetch") {
6969
7149
  return parseFetchCommand(this.getContext(), commandToken);
@@ -7056,6 +7236,9 @@ var Parser = class _Parser {
7056
7236
  args.push(this.parsePrimary());
7057
7237
  }
7058
7238
  const callNode = this.createCallExpression(createIdentifier(funcName, namePos), args);
7239
+ if (funcName === "closest" && args.length > 0 && this.match("to")) {
7240
+ callNode.closestTo = this.parseExpression();
7241
+ }
7059
7242
  if (funcName === "next" || funcName === "previous") {
7060
7243
  const relativePositional = this.parseRelativePositionalModifiers();
7061
7244
  if (relativePositional) {
@@ -7094,6 +7277,9 @@ var Parser = class _Parser {
7094
7277
  }
7095
7278
  parseTheXofY() {
7096
7279
  const positionalKeywords = ["first", "last", "next", "previous", "random", "closest"];
7280
+ if (this.isStyleRefStar(this.current)) {
7281
+ return this.parsePrimary();
7282
+ }
7097
7283
  if (!this.checkIdentifier()) {
7098
7284
  return this.createIdentifier("the");
7099
7285
  }
@@ -7109,7 +7295,7 @@ var Parser = class _Parser {
7109
7295
  return this.createIdentifier(propertyName);
7110
7296
  }
7111
7297
  this.advance();
7112
- const target = this.parseCall();
7298
+ const target = parseOfOperand(this.makePrattContext(), THE_OF_TARGET_BP);
7113
7299
  return {
7114
7300
  type: "propertyOfExpression",
7115
7301
  property: { type: "identifier", name: propertyName },
@@ -7304,6 +7490,13 @@ var Parser = class _Parser {
7304
7490
  checkLiteral() {
7305
7491
  return this.checkPredicate(isLiteral);
7306
7492
  }
7493
+ checkEventNamedCall() {
7494
+ if (this.isAtEnd() || !this.checkIdentifierLike() || this.checkIsCommand())
7495
+ return false;
7496
+ const token = this.peek();
7497
+ const next = this.tokens[this.current + 1];
7498
+ return next?.value === "(" && next.start === token.end;
7499
+ }
7307
7500
  checkIdentifierLike() {
7308
7501
  return this.checkPredicate(isIdentifierLike);
7309
7502
  }
@@ -7412,6 +7605,19 @@ var Parser = class _Parser {
7412
7605
  isAtEnd() {
7413
7606
  return this.current >= this.tokens.length;
7414
7607
  }
7608
+ atEndOfInput() {
7609
+ for (let i = this.current; i < this.tokens.length; i++) {
7610
+ if (!isComment(this.tokens[i]))
7611
+ return false;
7612
+ }
7613
+ return true;
7614
+ }
7615
+ checkFeatureStart() {
7616
+ if (this.isAtEnd())
7617
+ return false;
7618
+ const value = this.peek().value;
7619
+ return value === "init" || value === "on" || value === "def" || getRegisteredFeature(value) !== void 0;
7620
+ }
7415
7621
  peek() {
7416
7622
  if (this.isAtEnd()) {
7417
7623
  return { kind: "unknown", value: "", start: 0, end: 0, line: 1, column: 1 };
@@ -7616,6 +7822,7 @@ var Parser = class _Parser {
7616
7822
  match: this.match.bind(this),
7617
7823
  matchOperator: this.matchOperator.bind(this),
7618
7824
  isAtEnd: this.isAtEnd.bind(this),
7825
+ atEndOfInput: this.atEndOfInput.bind(this),
7619
7826
  createIdentifier: this.createIdentifier.bind(this),
7620
7827
  parseExpression: this.parseExpression.bind(this),
7621
7828
  parsePrimary: this.parsePrimary.bind(this),
@@ -7664,7 +7871,15 @@ Parser.POSTFIX_UNARY_OPERATORS = /* @__PURE__ */ new Set([
7664
7871
  "ignoring case"
7665
7872
  ]);
7666
7873
  Parser.PRATT_TABLE = PARSER_TABLE;
7874
+ Parser.BEEP_OPERAND_BP = 80;
7667
7875
  Parser.PSEUDO_COMMAND_PREPOSITIONS = ["from", "on", "with", "into", "at", "to"];
7876
+ Parser.POSITIONAL_SOURCE_HEADS = /* @__PURE__ */ new Set([
7877
+ "closest",
7878
+ "next",
7879
+ "previous",
7880
+ "first",
7881
+ "last"
7882
+ ]);
7668
7883
  function parse(input, options) {
7669
7884
  const tokens = tokenize(input);
7670
7885
  const parser = new Parser(tokens, options, input);
@@ -7846,6 +8061,84 @@ function isNodeList(value) {
7846
8061
  function isDocumentFragment(value) {
7847
8062
  return value !== null && typeof value === "object" && "nodeType" in value && value.nodeType === 11 && "childNodes" in value;
7848
8063
  }
8064
+ function beepValues(me, values) {
8065
+ const outputs = values.map(describeBeepValue);
8066
+ const shown = outputs.filter((output) => announce(me, output.value));
8067
+ if (shown.length === 0)
8068
+ return outputs;
8069
+ console.group("\u{1F514} beep! Debug Output");
8070
+ for (const output of shown) {
8071
+ console.log(`Value:`, output.value);
8072
+ console.log(`Type:`, output.type);
8073
+ console.log(`Representation:`, output.representation);
8074
+ console.log("---");
8075
+ }
8076
+ console.groupEnd();
8077
+ return outputs;
8078
+ }
8079
+ function announce(me, value) {
8080
+ const target = me;
8081
+ if (typeof CustomEvent !== "function" || typeof target?.dispatchEvent !== "function")
8082
+ return true;
8083
+ return target.dispatchEvent(new CustomEvent("hyperscript:beep", {
8084
+ bubbles: true,
8085
+ cancelable: true,
8086
+ detail: { element: me, value }
8087
+ }));
8088
+ }
8089
+ function describeBeepValue(value) {
8090
+ return { value, type: typeOf(value), representation: representationOf(value) };
8091
+ }
8092
+ function typeOf(value) {
8093
+ if (value === null)
8094
+ return "null";
8095
+ if (value === void 0)
8096
+ return "undefined";
8097
+ if (Array.isArray(value))
8098
+ return "array";
8099
+ if (isHTMLElement$2(value))
8100
+ return "HTMLElement";
8101
+ if (typeof Element !== "undefined" && value instanceof Element)
8102
+ return "Element";
8103
+ if (typeof Node !== "undefined" && value instanceof Node)
8104
+ return "Node";
8105
+ if (value instanceof Error)
8106
+ return "Error";
8107
+ if (value instanceof Date)
8108
+ return "Date";
8109
+ if (value instanceof RegExp)
8110
+ return "RegExp";
8111
+ return typeof value;
8112
+ }
8113
+ function representationOf(value) {
8114
+ if (value === null)
8115
+ return "null";
8116
+ if (value === void 0)
8117
+ return "undefined";
8118
+ if (Array.isArray(value)) {
8119
+ return `Array(${value.length}) [${value.slice(0, 3).map((v2) => representationOf(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
8120
+ }
8121
+ if (isHTMLElement$2(value)) {
8122
+ const el = value;
8123
+ const tag = el.tagName.toLowerCase();
8124
+ const id = el.id ? `#${el.id}` : "";
8125
+ const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
8126
+ return `<${tag}${id}${classes}/>`;
8127
+ }
8128
+ if (value instanceof Error)
8129
+ return `Error: ${value.message}`;
8130
+ if (typeof value === "string")
8131
+ return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
8132
+ if (typeof value === "object") {
8133
+ try {
8134
+ const keys = Object.keys(value);
8135
+ return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
8136
+ } catch {
8137
+ return "[Object]";
8138
+ }
8139
+ }
8140
+ return String(value);
8141
+ }
7849
8142
  function convertToNumber(value) {
7850
8143
  if (value === null || value === void 0) {
7851
8144
  return 0;
@@ -7877,6 +8170,16 @@ function convertToNumber(value) {
7877
8170
  }
7878
8171
  return 0;
7879
8172
  }
8173
+ function counterAmount(value) {
8174
+ return typeof value === "number" ? value : parseFloat(String(value));
8175
+ }
8176
+ function counterValue(value) {
8177
+ if (isHTMLElement$2(value)) {
8178
+ const element = value;
8179
+ return counterValue(element.value || element.textContent);
8180
+ }
8181
+ return value ? counterAmount(value) : 0;
8182
+ }
7880
8183
  function getVariableValue(name, context, preferredScope) {
7881
8184
  if (preferredScope === "element") {
7882
8185
  return getElementVar(context, name);
@@ -7978,32 +8281,36 @@ function setElementPropertyValue(propertyPath, value, context) {
7978
8281
  element[property] = value;
7979
8282
  }
7980
8283
  }
8284
+ function isObjectOwner(value) {
8285
+ return typeof value === "object" && value !== null && !Array.isArray(value) && !isHTMLElement$2(value);
8286
+ }
7981
8287
  function getCurrentNumericValue(target, property, scope, context) {
7982
8288
  if (typeof target === "number") {
7983
8289
  return target;
7984
8290
  }
7985
8291
  if (isHTMLElement$2(target)) {
7986
8292
  const element = target;
8293
+ if (property?.startsWith("@"))
8294
+ return counterValue(element.getAttribute(property.slice(1)));
7987
8295
  if (property) {
7988
8296
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
7989
- const value = element.getAttribute(property);
7990
- return convertToNumber(value);
8297
+ return counterValue(element.getAttribute(property));
7991
8298
  } else {
7992
- const value = element[property];
7993
- return convertToNumber(value);
8299
+ return counterValue(element[property]);
7994
8300
  }
7995
8301
  } else {
7996
- const value = element.value || element.textContent;
7997
- return convertToNumber(value);
8302
+ return counterValue(element);
7998
8303
  }
7999
8304
  }
8305
+ if (property && isObjectOwner(target)) {
8306
+ return counterValue(target[property]);
8307
+ }
8000
8308
  if (typeof target === "string") {
8001
8309
  if (scope === "element") {
8002
- return convertToNumber(getVariableValue(target, context, "element"));
8310
+ return counterValue(getVariableValue(target, context, "element"));
8003
8311
  }
8004
8312
  if (scope === "global") {
8005
- const value2 = getVariableValue(target, context, "global");
8006
- return convertToNumber(value2);
8313
+ return counterValue(getVariableValue(target, context, "global"));
8007
8314
  }
8008
8315
  if (target.includes(".")) {
8009
8316
  return getElementPropertyValue(target, context);
@@ -8015,14 +8322,17 @@ function getCurrentNumericValue(target, property, scope, context) {
8015
8322
  } else if (target === "you" && context.you) {
8016
8323
  return convertToNumber(context.you.value || 0);
8017
8324
  }
8018
- const value = getVariableValue(target, context);
8019
- return convertToNumber(value);
8325
+ return counterValue(getVariableValue(target, context));
8020
8326
  }
8021
- return convertToNumber(target);
8327
+ return counterValue(target);
8022
8328
  }
8023
8329
  function setTargetValue(target, property, scope, newValue, context) {
8024
8330
  if (isHTMLElement$2(target)) {
8025
8331
  const element = target;
8332
+ if (property?.startsWith("@")) {
8333
+ element.setAttribute(property.slice(1), String(newValue));
8334
+ return;
8335
+ }
8026
8336
  if (property) {
8027
8337
  if (property.startsWith("data-") || ["id", "class", "title", "alt", "src", "href"].includes(property)) {
8028
8338
  element.setAttribute(property, String(newValue));
@@ -8038,6 +8348,10 @@ function setTargetValue(target, property, scope, newValue, context) {
8038
8348
  }
8039
8349
  return;
8040
8350
  }
8351
+ if (property && isObjectOwner(target)) {
8352
+ target[property] = newValue;
8353
+ return;
8354
+ }
8041
8355
  if (typeof target === "string") {
8042
8356
  if (scope === "element") {
8043
8357
  setVariableValue(target, newValue, context, "element");
@@ -8452,13 +8766,6 @@ function isFunction(value) {
8452
8766
  function isInputElement$1(el) {
8453
8767
  return el !== null && typeof el === "object" && "tagName" in el && el.tagName === "INPUT";
8454
8768
  }
8455
- function isFormElement$1(el) {
8456
- if (el === null || typeof el !== "object" || !("tagName" in el)) {
8457
- return false;
8458
- }
8459
- const tag = el.tagName;
8460
- return tag === "INPUT" || tag === "SELECT" || tag === "TEXTAREA";
8461
- }
8462
8769
  function isHTMLElement$1(el) {
8463
8770
  return el !== null && typeof el === "object" && "nodeType" in el && el.nodeType === 1 && "style" in el;
8464
8771
  }
@@ -8509,7 +8816,7 @@ var SPECIAL_DOM_PROPERTIES = {
8509
8816
  innertext: (el) => el.textContent?.trim(),
8510
8817
  innerHTML: (el) => el.innerHTML,
8511
8818
  outerhtml: (el) => el.outerHTML,
8512
- value: (el) => isFormElement$1(el) ? el.value : void 0,
8819
+ value: (el) => "value" in el ? el.value : void 0,
8513
8820
  checked: (el) => isInputElement$1(el) ? el.checked : void 0,
8514
8821
  disabled: (el) => "disabled" in el ? el.disabled : void 0,
8515
8822
  selected: (el) => isOptionElement$1(el) ? el.selected : void 0,
@@ -9154,14 +9461,15 @@ var closestExpression = {
9154
9461
  if (typeof selector !== "string") {
9155
9462
  throw new Error("closest requires a string selector");
9156
9463
  }
9157
- if (!context.me) {
9464
+ const start = args.length > 1 ? args[1] : context.me;
9465
+ if (!(start instanceof Element)) {
9158
9466
  return null;
9159
9467
  }
9160
- return context.me.closest(selector);
9468
+ return start.closest(selector);
9161
9469
  },
9162
9470
  validate(args) {
9163
- if (args.length !== 1) {
9164
- return "closest requires exactly 1 argument (selector)";
9471
+ if (args.length !== 1 && args.length !== 2) {
9472
+ return "closest requires a selector and, optionally, the element to start from";
9165
9473
  }
9166
9474
  const arg = args[0];
9167
9475
  if (typeof arg === "string")
@@ -10625,7 +10933,7 @@ var isEmptyExpression = {
10625
10933
  else if (value instanceof Node || value instanceof Element)
10626
10934
  result = false;
10627
10935
  else if (isObject(value))
10628
- result = Object.keys(value).length === 0;
10936
+ result = value.length === 0;
10629
10937
  else
10630
10938
  result = false;
10631
10939
  return result;
@@ -11681,11 +11989,12 @@ async function evaluateBinaryExpression(node, context) {
11681
11989
  }
11682
11990
  }
11683
11991
  }
11684
- if (leftNode?.type === "selector" && leftNode.fromQuery && typeof leftNode.value === "string") {
11992
+ if (leftNode?.type === "selector" && typeof leftNode.value === "string" && (leftNode.fromQuery || operator === "in" && leftNode.value.startsWith("."))) {
11685
11993
  const root = await evaluateAST(node.right, context);
11686
11994
  const scope = root && typeof root.querySelectorAll === "function" ? root : null;
11687
11995
  if (scope) {
11688
- return Array.from(scope.querySelectorAll(leftNode.value));
11996
+ const query = leftNode.fromQuery ? leftNode.value : escapeSelectorForQuery(leftNode, leftNode.value);
11997
+ return Array.from(scope.querySelectorAll(query));
11689
11998
  }
11690
11999
  }
11691
12000
  }
@@ -11694,6 +12003,12 @@ async function evaluateBinaryExpression(node, context) {
11694
12003
  return getExpr(context, "styleRef").evaluate(context, leftNode.value.slice(1), el);
11695
12004
  }
11696
12005
  if (operator === "of") {
12006
+ if (leftNode?.type === "attributeAccess") {
12007
+ const target = await evaluateAST(node.right, context);
12008
+ if (target == null)
12009
+ return void 0;
12010
+ return getExpr(context, "possessive").evaluate(context, target, `@${leftNode.attributeName}`);
12011
+ }
11697
12012
  const path = propertyPathOf(node.left);
11698
12013
  if (path) {
11699
12014
  const target = await evaluateAST(node.right, context);
@@ -11701,6 +12016,10 @@ async function evaluateBinaryExpression(node, context) {
11701
12016
  for (const seg of path) {
11702
12017
  if (cur == null)
11703
12018
  return void 0;
12019
+ if (isMappableCollection(cur) && !(seg in cur)) {
12020
+ cur = await getExpr(context, "possessive").evaluate(context, cur, seg);
12021
+ continue;
12022
+ }
11704
12023
  cur = isElement(cur) ? getElementProperty$1(cur, seg) : cur[seg];
11705
12024
  }
11706
12025
  return typeof cur === "function" ? cur.bind(target) : cur;
@@ -11725,26 +12044,21 @@ async function evaluateBinaryExpression(node, context) {
11725
12044
  const R = node.ignoringCase ? applyCI(right) : right;
11726
12045
  switch (operator) {
11727
12046
  case "+": {
11728
- let addLeft = left;
11729
- let addRight = right;
11730
12047
  if (node.coerceNumeric) {
11731
- if (typeof addLeft === "string")
11732
- addLeft = convertToNumber(addLeft);
11733
- if (typeof addRight === "string")
11734
- addRight = convertToNumber(addRight);
12048
+ return counterValue(left) + counterAmount(right);
11735
12049
  }
11736
- if (typeof addLeft === "string" || typeof addRight === "string") {
11737
- return String(addLeft ?? "") + String(addRight ?? "");
12050
+ if (typeof left === "string" || typeof right === "string") {
12051
+ return String(left ?? "") + String(right ?? "");
11738
12052
  }
11739
- if (Array.isArray(addLeft)) {
11740
- return Array.isArray(addRight) ? [...addLeft, ...addRight] : [...addLeft, addRight];
12053
+ if (Array.isArray(left)) {
12054
+ return Array.isArray(right) ? [...left, ...right] : [...left, right];
11741
12055
  }
11742
- return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, {
11743
- left: addLeft,
11744
- right: addRight
11745
- }));
12056
+ return unwrapTypedResult(await getExpr(context, "addition").evaluate(context, { left, right }));
11746
12057
  }
11747
12058
  case "-":
12059
+ if (node.coerceNumeric) {
12060
+ return counterValue(left) - counterAmount(right);
12061
+ }
11748
12062
  return unwrapTypedResult(await getExpr(context, "subtraction").evaluate(context, { left, right }));
11749
12063
  case "*":
11750
12064
  return unwrapTypedResult(await getExpr(context, "multiplication").evaluate(context, { left, right }));
@@ -11989,15 +12303,14 @@ async function evaluateAttributeAccessNode(node, context) {
11989
12303
  return `@${attributeName}`;
11990
12304
  }
11991
12305
  async function evaluatePropertyOfExpressionNode(node, context) {
11992
- const propertyNode = node.property;
11993
- if (propertyNode?.type !== "identifier") {
12306
+ const propertyNode2 = node.property;
12307
+ if (propertyNode2?.type !== "identifier") {
11994
12308
  throw new Error('Property name must be an identifier in "the X of Y" pattern');
11995
12309
  }
11996
- const propertyName = propertyNode.name;
12310
+ const propertyName = propertyNode2.name;
11997
12311
  const target = await evaluateAST(node.target, context);
11998
- if (target == null) {
11999
- throw new Error(`Cannot access property "${propertyName}" of ${target}`);
12000
- }
12312
+ if (target == null)
12313
+ return void 0;
12001
12314
  return getExpr(context, "possessive").evaluate(context, target, propertyName);
12002
12315
  }
12003
12316
  async function evaluateTemplateLiteralNode(node, context) {
@@ -12113,6 +12426,9 @@ async function evaluateUnaryExpression(node, context) {
12113
12426
  case "not":
12114
12427
  case "!":
12115
12428
  return logicalExpressions.not.evaluate(context, value);
12429
+ case "beep!":
12430
+ beepValues(context.me, [value]);
12431
+ return value;
12116
12432
  case "no":
12117
12433
  return logicalExpressions.no.evaluate(context, value);
12118
12434
  case "-":
@@ -12201,6 +12517,10 @@ async function evaluateCallExpression(node, context) {
12201
12517
  const args = await resolveCallArgs(node.arguments, funcName, context);
12202
12518
  switch (funcName) {
12203
12519
  case "closest":
12520
+ if (node.closestTo) {
12521
+ const start = unwrapElement(await evaluateAST(node.closestTo, context)) ?? null;
12522
+ return referencesExpressions.closest.evaluate(context, args[0], start);
12523
+ }
12204
12524
  return referencesExpressions.closest.evaluate(context, ...args);
12205
12525
  case "previous":
12206
12526
  case "next": {
@@ -13156,8 +13476,8 @@ var RuntimeBase = class _RuntimeBase {
13156
13476
  }
13157
13477
  case "initBlock":
13158
13478
  case "block": {
13159
- const block = node;
13160
- const ops = (Array.isArray(block.commands) ? block.commands : []).map((c) => this.compile(c));
13479
+ const block2 = node;
13480
+ const ops = (Array.isArray(block2.commands) ? block2.commands : []).map((c) => this.compile(c));
13161
13481
  const consumesHalt = node.type === "initBlock";
13162
13482
  return async (context) => {
13163
13483
  for (const op of ops) {
@@ -13537,12 +13857,22 @@ var RuntimeBase = class _RuntimeBase {
13537
13857
  debug.runtime(`BEHAVIOR: Finished installing ${behaviorName}`);
13538
13858
  }
13539
13859
  async executeEventHandler(node, context) {
13540
- const { event, events, commands, target, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
13860
+ const { event, events, commands, target, targetExpression, args, selector, condition, attributeName, watchTarget, modifiers, errorSymbol, errorHandler, finallyHandler } = node;
13541
13861
  const eventNames = events && events.length > 0 ? events : [event];
13542
13862
  debug.runtime(`BEHAVIOR: executeEventHandler: event='${event}', target='${target}'`);
13543
13863
  let targets = [];
13544
13864
  let globalTarget = null;
13545
- if (target) {
13865
+ if (targetExpression) {
13866
+ const resolved = await this.execute(targetExpression, context);
13867
+ if (resolved === window)
13868
+ globalTarget = window;
13869
+ else if (resolved === document)
13870
+ globalTarget = document;
13871
+ else if (this.isElement(resolved))
13872
+ targets = [resolved];
13873
+ else if (Array.isArray(resolved))
13874
+ targets = resolved.filter((el) => this.isElement(el));
13875
+ } else if (target) {
13546
13876
  const targetLower = typeof target === "string" ? target.toLowerCase() : "";
13547
13877
  if (targetLower === "window" || targetLower === "the window") {
13548
13878
  globalTarget = window;
@@ -15450,12 +15780,79 @@ function parseAttribute(expression) {
15450
15780
  }
15451
15781
  throw new Error(`Invalid attribute syntax: ${expression}`);
15452
15782
  }
15453
- function parseAttributeName(expression) {
15454
- return parseAttribute(expression).name;
15783
+ function parseAttributeName(expression) {
15784
+ return parseAttribute(expression).name;
15785
+ }
15786
+ function parseAttributeWithValue(expression) {
15787
+ const result = parseAttribute(expression);
15788
+ return { name: result.name, value: result.value ?? "" };
15789
+ }
15790
+ function _parseDurationComponents(value) {
15791
+ const trimmed = value.trim();
15792
+ const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
15793
+ if (!match) {
15794
+ return null;
15795
+ }
15796
+ const number = parseFloat(match[1]);
15797
+ const unit = (match[2] || "ms").toLowerCase();
15798
+ if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
15799
+ return { number, milliseconds: Math.floor(number) };
15800
+ } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
15801
+ return { number, milliseconds: Math.floor(number * 1e3) };
15802
+ }
15803
+ return null;
15804
+ }
15805
+ function parseDuration(value, defaultMs = 300) {
15806
+ if (typeof value === "number") {
15807
+ return Math.max(0, Math.floor(value));
15808
+ }
15809
+ if (typeof value === "string") {
15810
+ const result = _parseDurationComponents(value);
15811
+ return result ? result.milliseconds : defaultMs;
15812
+ }
15813
+ return defaultMs;
15814
+ }
15815
+ function parseDurationStrict(value) {
15816
+ if (typeof value === "number") {
15817
+ if (value < 0)
15818
+ throw new Error("Duration must be >= 0");
15819
+ return Math.floor(value);
15820
+ }
15821
+ if (typeof value === "string") {
15822
+ const result = _parseDurationComponents(value);
15823
+ if (!result) {
15824
+ throw new Error(`Invalid duration format: "${value}"`);
15825
+ }
15826
+ return result.milliseconds;
15827
+ }
15828
+ throw new Error(`Invalid duration type: ${typeof value}`);
15829
+ }
15830
+ function parseCSSDurations(durationString) {
15831
+ if (!durationString || durationString === "none") {
15832
+ return [0];
15833
+ }
15834
+ return durationString.split(",").map((duration) => {
15835
+ const value = parseFloat(duration.trim());
15836
+ if (isNaN(value))
15837
+ return 0;
15838
+ if (duration.includes("s") && !duration.includes("ms")) {
15839
+ return value * 1e3;
15840
+ }
15841
+ return value;
15842
+ });
15843
+ }
15844
+ function calculateMaxAnimationTime(durations, delays) {
15845
+ let maxTime = 0;
15846
+ for (let i = 0; i < durations.length; i++) {
15847
+ const duration = durations[i] || 0;
15848
+ const delay = delays[i] || 0;
15849
+ const totalTime = duration + delay;
15850
+ maxTime = Math.max(maxTime, totalTime);
15851
+ }
15852
+ return maxTime;
15455
15853
  }
15456
- function parseAttributeWithValue(expression) {
15457
- const result = parseAttribute(expression);
15458
- return { name: result.name, value: result.value ?? "" };
15854
+ function camelToKebab(str) {
15855
+ return str.replace(/([A-Z])/g, "-$1").toLowerCase();
15459
15856
  }
15460
15857
  function batchApply(targets, operation) {
15461
15858
  for (const element of targets) {
@@ -15519,15 +15916,18 @@ function toggleAttribute(element, name, value) {
15519
15916
  }
15520
15917
  }
15521
15918
  }
15919
+ function toCssPropertyName(property) {
15920
+ return property.startsWith("--") ? property : camelToKebab(property);
15921
+ }
15522
15922
  function batchSetStyles(targets, styles) {
15523
15923
  const entries = Object.entries(styles);
15524
15924
  return batchApplyItems(targets, entries, (element, [property, value]) => {
15525
- element.style.setProperty(property, value);
15925
+ element.style.setProperty(toCssPropertyName(property), value);
15526
15926
  });
15527
15927
  }
15528
15928
  function batchRemoveStyles(targets, properties) {
15529
15929
  return batchApplyItems(targets, properties, (element, property) => {
15530
- element.style.removeProperty(property);
15930
+ element.style.removeProperty(toCssPropertyName(property));
15531
15931
  });
15532
15932
  }
15533
15933
  function parseClasses(classValue) {
@@ -16164,73 +16564,6 @@ var RemoveCommand = (() => {
16164
16564
  return RemoveCommand2 = _classThis;
16165
16565
  })();
16166
16566
  var createRemoveCommand = createFactory(RemoveCommand);
16167
- function _parseDurationComponents(value) {
16168
- const trimmed = value.trim();
16169
- const match = trimmed.match(/^(\d+(?:\.\d+)?)\s*(s|ms|sec|seconds?|milliseconds?)?$/i);
16170
- if (!match) {
16171
- return null;
16172
- }
16173
- const number = parseFloat(match[1]);
16174
- const unit = (match[2] || "ms").toLowerCase();
16175
- if (unit === "ms" || unit === "millisecond" || unit === "milliseconds") {
16176
- return { number, milliseconds: Math.floor(number) };
16177
- } else if (unit === "s" || unit === "sec" || unit === "second" || unit === "seconds") {
16178
- return { number, milliseconds: Math.floor(number * 1e3) };
16179
- }
16180
- return null;
16181
- }
16182
- function parseDuration(value, defaultMs = 300) {
16183
- if (typeof value === "number") {
16184
- return Math.max(0, Math.floor(value));
16185
- }
16186
- if (typeof value === "string") {
16187
- const result = _parseDurationComponents(value);
16188
- return result ? result.milliseconds : defaultMs;
16189
- }
16190
- return defaultMs;
16191
- }
16192
- function parseDurationStrict(value) {
16193
- if (typeof value === "number") {
16194
- if (value < 0)
16195
- throw new Error("Duration must be >= 0");
16196
- return Math.floor(value);
16197
- }
16198
- if (typeof value === "string") {
16199
- const result = _parseDurationComponents(value);
16200
- if (!result) {
16201
- throw new Error(`Invalid duration format: "${value}"`);
16202
- }
16203
- return result.milliseconds;
16204
- }
16205
- throw new Error(`Invalid duration type: ${typeof value}`);
16206
- }
16207
- function parseCSSDurations(durationString) {
16208
- if (!durationString || durationString === "none") {
16209
- return [0];
16210
- }
16211
- return durationString.split(",").map((duration) => {
16212
- const value = parseFloat(duration.trim());
16213
- if (isNaN(value))
16214
- return 0;
16215
- if (duration.includes("s") && !duration.includes("ms")) {
16216
- return value * 1e3;
16217
- }
16218
- return value;
16219
- });
16220
- }
16221
- function calculateMaxAnimationTime(durations, delays) {
16222
- let maxTime = 0;
16223
- for (let i = 0; i < durations.length; i++) {
16224
- const duration = durations[i] || 0;
16225
- const delay = delays[i] || 0;
16226
- const totalTime = duration + delay;
16227
- maxTime = Math.max(maxTime, totalTime);
16228
- }
16229
- return maxTime;
16230
- }
16231
- function camelToKebab(str) {
16232
- return str.replace(/([A-Z])/g, "-$1").toLowerCase();
16233
- }
16234
16567
  function detectSmartElementType(elements) {
16235
16568
  if (elements.length === 0)
16236
16569
  return null;
@@ -16544,13 +16877,13 @@ async function resolveAnyPropertyTarget(node, evaluator, context) {
16544
16877
  const anyNode = node;
16545
16878
  if (anyNode?.type === "possessiveExpression" || anyNode?.type === "memberExpression") {
16546
16879
  const objectNode = anyNode.object;
16547
- const propertyNode = anyNode.property;
16880
+ const propertyNode2 = anyNode.property;
16548
16881
  let element = await evaluator.evaluate(objectNode, context);
16549
16882
  if (Array.isArray(element))
16550
16883
  element = element[0];
16551
16884
  if (!isHTMLElement$2(element))
16552
16885
  return null;
16553
- const propertyName = propertyNode?.name || propertyNode?.value;
16886
+ const propertyName = propertyNode2?.name || propertyNode2?.value;
16554
16887
  if (!propertyName)
16555
16888
  return null;
16556
16889
  return { element, property: propertyName };
@@ -16668,9 +17001,9 @@ var ToggleCommand = (() => {
16668
17001
  if (!raw.args?.length)
16669
17002
  throw new Error("toggle command requires an argument");
16670
17003
  const firstArg = raw.args[0];
16671
- const propertyTarget = await resolveAnyPropertyTarget(firstArg, evaluator, context);
16672
- if (propertyTarget) {
16673
- return { type: "property", target: propertyTarget };
17004
+ const propertyTarget2 = await resolveAnyPropertyTarget(firstArg, evaluator, context);
17005
+ if (propertyTarget2) {
17006
+ return { type: "property", target: propertyTarget2 };
16674
17007
  }
16675
17008
  if (firstArg.type === "selector" && typeof firstArg.value === "string" && firstArg.value.startsWith("*")) {
16676
17009
  let expression2 = firstArg.value;
@@ -16904,13 +17237,13 @@ var PutCommand = (() => {
16904
17237
  let targetSelector = null, memberPath, variableName;
16905
17238
  if (targetArg) {
16906
17239
  const tt = nodeType(targetArg);
16907
- const propertyTarget = await resolveAnyPropertyTarget(targetArg, evaluator, context);
16908
- if (propertyTarget) {
17240
+ const propertyTarget2 = await resolveAnyPropertyTarget(targetArg, evaluator, context);
17241
+ if (propertyTarget2) {
16909
17242
  return {
16910
17243
  value,
16911
- targets: [propertyTarget.element],
17244
+ targets: [propertyTarget2.element],
16912
17245
  position: "into",
16913
- memberPath: propertyTarget.property
17246
+ memberPath: propertyTarget2.property
16914
17247
  };
16915
17248
  }
16916
17249
  if (tt === "memberExpression") {
@@ -17050,7 +17383,11 @@ var PutCommand = (() => {
17050
17383
  }
17051
17384
  if (isInsertableNode(v2))
17052
17385
  return v2;
17053
- return v2 == null ? "" : String(v2);
17386
+ if (v2 == null)
17387
+ return "null";
17388
+ if (Array.isArray(v2))
17389
+ return arrayFragment(v2);
17390
+ return String(v2);
17054
17391
  }
17055
17392
  looksLikeCss(s) {
17056
17393
  if (!s)
@@ -17135,6 +17472,19 @@ var PutCommand = (() => {
17135
17472
  })();
17136
17473
  return PutCommand2 = _classThis;
17137
17474
  })();
17475
+ function arrayFragment(items) {
17476
+ const fragment = document.createDocumentFragment();
17477
+ for (const item of items) {
17478
+ if (isInsertableNode(item)) {
17479
+ fragment.append(item);
17480
+ continue;
17481
+ }
17482
+ const template = document.createElement("template");
17483
+ template.innerHTML = item === null ? "" : String(item);
17484
+ fragment.append(template.content);
17485
+ }
17486
+ return fragment;
17487
+ }
17138
17488
  var createPutCommand = createFactory(PutCommand);
17139
17489
  var HTML_ESCAPE = {
17140
17490
  "&": "&amp;",
@@ -18321,10 +18671,28 @@ var STRATEGY_KEYWORDS = {
18321
18671
  innermorph: "morph",
18322
18672
  outermorph: "morphOuter"
18323
18673
  };
18674
+ function loneRootWithTag(content, tagName) {
18675
+ let root;
18676
+ if (typeof content === "string") {
18677
+ const template = document.createElement("template");
18678
+ template.innerHTML = content;
18679
+ root = template.content.firstElementChild;
18680
+ if (!root || root.nextElementSibling)
18681
+ return null;
18682
+ } else {
18683
+ root = content.cloneNode(true);
18684
+ }
18685
+ return root.tagName === tagName && isHTMLElement$2(root) ? root : null;
18686
+ }
18324
18687
  function executeSwap(target, content, strategy, morphOptions) {
18325
18688
  const contentStr = content !== null && !isHTMLElement$2(content) ? content : "";
18326
18689
  const contentEl = isHTMLElement$2(content) ? content : null;
18327
18690
  switch (strategy) {
18691
+ case "morphMerge": {
18692
+ const root = content !== null ? loneRootWithTag(content, target.tagName) : null;
18693
+ executeSwap(target, root ?? content, root ? "morphOuter" : "morph", morphOptions);
18694
+ break;
18695
+ }
18328
18696
  case "morph":
18329
18697
  if (content !== null) {
18330
18698
  try {
@@ -18594,7 +18962,7 @@ var MorphCommand = (() => {
18594
18962
  if (args.length === 0 && modifiers.with === void 0) {
18595
18963
  throw new Error("[HyperFixi] morph: command requires arguments");
18596
18964
  }
18597
- const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morph";
18965
+ const strategy = literalText(modifiers.strategy)?.toLowerCase() === "over" ? "morphOuter" : "morphMerge";
18598
18966
  const useViewTransition = modifiers.viewTransition !== void 0;
18599
18967
  const targetNode = modifiers.on !== void 0 ? modifiers.on : args[0];
18600
18968
  const contentNode = modifiers.with !== void 0 ? modifiers.with : modifiers.on !== void 0 ? args[0] : args[1];
@@ -19862,6 +20230,19 @@ async function resolveWriteTarget(node, evaluator, context, options) {
19862
20230
  }
19863
20231
  return { kind: "property", target: property };
19864
20232
  }
20233
+ if (options.ofAndObjectProperty) {
20234
+ const access = ofOrPossessiveAccess(n);
20235
+ if (access) {
20236
+ const owner = await evaluator.evaluate(access.owner, context);
20237
+ const element = Array.isArray(owner) ? owner[0] : owner;
20238
+ if (access.isOf && isHTMLElement$2(element)) {
20239
+ return { kind: "property", target: { element, property: access.name } };
20240
+ }
20241
+ if (isObjectOwner(owner)) {
20242
+ return { kind: "member", container: owner, property: access.name };
20243
+ }
20244
+ }
20245
+ }
19865
20246
  if (options.bareReference && (nodeType === "identifier" || nodeType === "variable" || nodeType === "symbol") && typeof n.name === "string") {
19866
20247
  const rawScope = n.scope;
19867
20248
  const scope = rawScope === "element" || rawScope === "global" ? rawScope : void 0;
@@ -19869,6 +20250,13 @@ async function resolveWriteTarget(node, evaluator, context, options) {
19869
20250
  }
19870
20251
  return null;
19871
20252
  }
20253
+ function ofOrPossessiveAccess(n) {
20254
+ const [owner, property, isOf] = n.type === "possessiveExpression" ? [n.object, n.property, false] : n.type === "propertyOfExpression" ? [n.target, n.property, false] : n.type === "binaryExpression" && n.operator === "of" ? [n.right, n.left, true] : [void 0, void 0, false];
20255
+ const name = property?.name;
20256
+ if (!owner || typeof name !== "string" || !/^[A-Za-z_$][\w$]*$/.test(name))
20257
+ return null;
20258
+ return { owner, name, isOf };
20259
+ }
19872
20260
  function toSetInput(target, value) {
19873
20261
  switch (target.kind) {
19874
20262
  case "node-write":
@@ -19889,10 +20277,18 @@ function toSetInput(target, value) {
19889
20277
  property: target.target.property,
19890
20278
  value
19891
20279
  };
20280
+ case "member":
20281
+ return {
20282
+ type: "member-assignment",
20283
+ container: target.container,
20284
+ property: target.property,
20285
+ value
20286
+ };
19892
20287
  default:
19893
20288
  throw new Error(`set: unrequested write-target rung '${target.kind}'`);
19894
20289
  }
19895
20290
  }
20291
+ var SET_RUNGS = { nodeWriters: true, styleSplit: true, ofAndObjectProperty: true };
19896
20292
  var SetCommand = (() => {
19897
20293
  let _classDecorators = [command({ name: "set" })];
19898
20294
  let _classDescriptor;
@@ -19911,8 +20307,7 @@ var SetCommand = (() => {
19911
20307
  const argScope = firstArg?.scope;
19912
20308
  const writeTarget = await resolveWriteTarget(firstArg, evaluator, context, {
19913
20309
  scopeElements: () => this.resolveTargets(raw.modifiers.on, evaluator, context),
19914
- nodeWriters: true,
19915
- styleSplit: true
20310
+ ...SET_RUNGS
19916
20311
  });
19917
20312
  if (writeTarget) {
19918
20313
  return toSetInput(writeTarget, await this.extractValue(raw, evaluator, context));
@@ -20036,13 +20431,13 @@ var SetCommand = (() => {
20036
20431
  }
20037
20432
  async tryParseMemberExpression(firstArg, raw, evaluator, context) {
20038
20433
  const objectNode = firstArg.object;
20039
- const propertyNode = firstArg.property;
20040
- if (objectNode?.name && propertyNode?.name) {
20434
+ const propertyNode2 = firstArg.property;
20435
+ if (objectNode?.name && propertyNode2?.name) {
20041
20436
  const objectName = objectNode.name.toLowerCase();
20042
20437
  if (["me", "my", "it", "its", "you", "your"].includes(objectName)) {
20043
20438
  const element = resolvePossessive(objectName, context);
20044
20439
  const value = await this.extractValue(raw, evaluator, context);
20045
- return { type: "property", element, property: propertyNode.name, value };
20440
+ return { type: "property", element, property: propertyNode2.name, value };
20046
20441
  }
20047
20442
  }
20048
20443
  const objectAst = firstArg.object;
@@ -20058,7 +20453,7 @@ var SetCommand = (() => {
20058
20453
  container = await evaluator.evaluate(objectAst, context);
20059
20454
  }
20060
20455
  if (container != null && typeof container === "object") {
20061
- const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode?.name || "";
20456
+ const property = computed ? String(await evaluator.evaluate(firstArg.property, context)) : propertyNode2?.name || "";
20062
20457
  if (property) {
20063
20458
  const value = await this.extractValue(raw, evaluator, context);
20064
20459
  return {
@@ -20213,12 +20608,24 @@ function getNodeType(node) {
20213
20608
  return "unknown";
20214
20609
  return node.type || "unknown";
20215
20610
  }
20611
+ function propertyTarget(node) {
20612
+ const n = node;
20613
+ const owner = n.type === "propertyOfExpression" ? n.target : n.type === "possessiveExpression" || n.type === "memberExpression" || n.type === "propertyAccess" ? n.object : void 0;
20614
+ if (!owner || n.computed)
20615
+ return null;
20616
+ const p = n.property;
20617
+ const name = typeof p === "string" ? p : typeof p?.name === "string" ? p.name : void 0;
20618
+ if (!name || !/^[A-Za-z_$][\w$-]*$/.test(name))
20619
+ return null;
20620
+ return { owner, name };
20621
+ }
20216
20622
  async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20217
20623
  if (!raw.args || raw.args.length === 0) {
20218
20624
  throw new Error(`${commandName} command requires a target`);
20219
20625
  }
20220
20626
  const targetArg = raw.args[0];
20221
20627
  let target;
20628
+ let property;
20222
20629
  let extractedScope;
20223
20630
  let customWrite;
20224
20631
  const nodeType = getNodeType(targetArg);
@@ -20231,7 +20638,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20231
20638
  target = targetArg.value;
20232
20639
  } else {
20233
20640
  const writer = getRegisteredNodeWriter(nodeType);
20234
- if (writer) {
20641
+ const attribute = writer ? null : await resolveAttributeWriteTarget(targetArg, evaluator, context, async () => context.me ? [context.me] : []);
20642
+ const prop = writer || attribute ? null : propertyTarget(targetArg);
20643
+ const owner = prop ? await evaluator.evaluate(prop.owner, context) : void 0;
20644
+ const ownerElement = Array.isArray(owner) ? owner[0] : owner;
20645
+ if (attribute?.elements[0]) {
20646
+ target = attribute.elements[0];
20647
+ property = `@${attribute.name}`;
20648
+ } else if (prop && isHTMLElement$2(ownerElement)) {
20649
+ target = ownerElement;
20650
+ property = prop.name;
20651
+ } else if (prop && isObjectOwner(owner)) {
20652
+ target = owner;
20653
+ property = prop.name;
20654
+ } else if (writer) {
20235
20655
  const currentRaw = await evaluator.evaluate(targetArg, context);
20236
20656
  const currentValue = typeof currentRaw === "number" ? currentRaw : currentRaw == null ? 0 : Number(currentRaw) || 0;
20237
20657
  target = currentValue;
@@ -20260,24 +20680,20 @@ async function parseNumericTargetInput(raw, evaluator, context, commandName) {
20260
20680
  const evaluated = await evaluator.evaluate(arg, context);
20261
20681
  if (typeof evaluated === "number") {
20262
20682
  amount = evaluated;
20683
+ } else if (evaluated == null) {
20684
+ amount = NaN;
20263
20685
  }
20264
20686
  }
20265
20687
  }
20266
20688
  const byModifier = raw.modifiers?.by;
20267
20689
  if (byModifier) {
20268
- const byValue = byModifier.value;
20269
- if (getNodeType(byModifier) === "literal" && typeof byValue === "number") {
20270
- amount = byValue;
20271
- } else if (getNodeType(byModifier) !== "literal") {
20272
- const evaluated = await evaluator.evaluate(byModifier, context);
20273
- if (typeof evaluated === "number") {
20274
- amount = evaluated;
20275
- }
20276
- }
20690
+ const value = getNodeType(byModifier) === "literal" ? byModifier.value : await evaluator.evaluate(byModifier, context);
20691
+ amount = counterAmount(value);
20277
20692
  }
20278
20693
  return {
20279
20694
  target,
20280
20695
  amount,
20696
+ ...property && { property },
20281
20697
  ...scope && { scope },
20282
20698
  ...customWrite && { customWrite }
20283
20699
  };
@@ -20299,7 +20715,7 @@ var NumericModifyCommand = (() => {
20299
20715
  async execute(input, context) {
20300
20716
  const { target, property, scope, amount = 1, operation = "increment", customWrite } = input;
20301
20717
  if (customWrite) {
20302
- const delta = isFinite(amount) ? amount : 1;
20718
+ const delta = amount;
20303
20719
  const newValue2 = operation === "increment" ? customWrite.currentValue + delta : customWrite.currentValue - delta;
20304
20720
  await customWrite.writer(customWrite.node, newValue2, context);
20305
20721
  Object.assign(context, { it: newValue2 });
@@ -20310,8 +20726,7 @@ var NumericModifyCommand = (() => {
20310
20726
  if (isNaN(currentValue)) {
20311
20727
  newValue = NaN;
20312
20728
  } else {
20313
- const delta = isFinite(amount) ? amount : 1;
20314
- newValue = operation === "increment" ? currentValue + delta : currentValue - delta;
20729
+ newValue = operation === "increment" ? currentValue + amount : currentValue - amount;
20315
20730
  }
20316
20731
  setTargetValue(target, property, scope, newValue, context);
20317
20732
  Object.assign(context, { it: newValue });
@@ -20737,9 +21152,9 @@ var GoCommand = (() => {
20737
21152
  }
20738
21153
  const { target, position, offset, behavior } = input;
20739
21154
  if (typeof window !== "undefined") {
20740
- const block = this.mapVerticalPosition(position.vertical);
21155
+ const block2 = this.mapVerticalPosition(position.vertical);
20741
21156
  const inline = this.mapHorizontalPosition(position.horizontal);
20742
- target.scrollIntoView?.({ behavior, block, inline });
21157
+ target.scrollIntoView?.({ behavior, block: block2, inline });
20743
21158
  if (offset !== 0) {
20744
21159
  const { x, y } = this.calculateScrollPosition(target, position, offset);
20745
21160
  window.scrollTo?.({ left: x, top: y, behavior });
@@ -21374,7 +21789,10 @@ function createUntilLoopConfig(conditionExpr, evaluateCondition2, context, index
21374
21789
  };
21375
21790
  const config2 = {
21376
21791
  type: "until",
21377
- shouldContinue: () => !evaluateCondition2(conditionExpr, context)
21792
+ shouldContinue: () => {
21793
+ const met = evaluateCondition2(conditionExpr, context);
21794
+ return met instanceof Promise ? met.then((value) => !value) : !met;
21795
+ }
21378
21796
  };
21379
21797
  return { config: config2, iterCtx };
21380
21798
  }
@@ -21409,7 +21827,7 @@ function createForeverLoopConfig(indexVariable, maxIterations = 1e4) {
21409
21827
  };
21410
21828
  return { config: config2, iterCtx };
21411
21829
  }
21412
- async function slotText(node, evaluator, context) {
21830
+ async function slotText$1(node, evaluator, context) {
21413
21831
  if (!node)
21414
21832
  return void 0;
21415
21833
  const n = node;
@@ -21422,6 +21840,18 @@ async function slotText(node, evaluator, context) {
21422
21840
  const v2 = await evaluator.evaluate(node, context);
21423
21841
  return typeof v2 === "string" ? v2 : void 0;
21424
21842
  }
21843
+ function isEventTarget(value) {
21844
+ return typeof value?.addEventListener === "function";
21845
+ }
21846
+ async function evaluateLoopCondition(condition, context) {
21847
+ if (typeof condition === "function")
21848
+ return Boolean(await condition());
21849
+ return evaluateCondition(condition, context);
21850
+ }
21851
+ function loopCount(value) {
21852
+ const count = Number(value);
21853
+ return Number.isNaN(count) ? 0 : count;
21854
+ }
21425
21855
  var RepeatCommand = (() => {
21426
21856
  let _classDecorators = [command({ name: "repeat" })];
21427
21857
  let _classDescriptor;
@@ -21458,7 +21888,7 @@ var RepeatCommand = (() => {
21458
21888
  }
21459
21889
  }
21460
21890
  const m = raw.modifiers ?? {};
21461
- const text = (node) => slotText(node, evaluator, context);
21891
+ const text = (node) => slotText$1(node, evaluator, context);
21462
21892
  const loopType = await text(m.loopType) ?? null;
21463
21893
  const bottomTested = m.bottomTested ? await evaluator.evaluate(m.bottomTested, context) : false;
21464
21894
  if (loopType === "for" || m.for) {
@@ -21476,17 +21906,24 @@ var RepeatCommand = (() => {
21476
21906
  };
21477
21907
  }
21478
21908
  if (loopType === "times" || m.times) {
21479
- const countValue = m.times && await evaluator.evaluate(m.times, context);
21480
- const count = typeof countValue === "number" ? countValue : parseInt(String(countValue), 10);
21481
- if (isNaN(count))
21909
+ if (!m.times)
21482
21910
  throw new Error("times loops require a count number");
21911
+ const count = loopCount(await evaluator.evaluate(m.times, context));
21483
21912
  return { type: "times", count, indexVariable, commands, elseCommands };
21484
21913
  }
21485
21914
  if (loopType === "while" || m.while) {
21486
21915
  const condition = m.while;
21487
21916
  if (!condition)
21488
21917
  throw new Error("while loops require a condition");
21489
- return { type: "while", condition, indexVariable, commands, elseCommands, bottomTested };
21918
+ const check = () => evaluator.evaluate(condition, context);
21919
+ return {
21920
+ type: "while",
21921
+ condition: check,
21922
+ indexVariable,
21923
+ commands,
21924
+ elseCommands,
21925
+ bottomTested
21926
+ };
21490
21927
  }
21491
21928
  if (loopType === "until-event" || m.event) {
21492
21929
  const eventName = await text(m.event);
@@ -21502,7 +21939,7 @@ var RepeatCommand = (() => {
21502
21939
  eventTarget = window;
21503
21940
  } else {
21504
21941
  const target = await evaluator.evaluate(m.from, context);
21505
- if (target instanceof EventTarget)
21942
+ if (isEventTarget(target))
21506
21943
  eventTarget = target;
21507
21944
  else if (target === "document")
21508
21945
  eventTarget = document;
@@ -21523,7 +21960,15 @@ var RepeatCommand = (() => {
21523
21960
  const condition = m.until;
21524
21961
  if (!condition)
21525
21962
  throw new Error("until loops require a condition");
21526
- return { type: "until", condition, indexVariable, commands, elseCommands, bottomTested };
21963
+ const check = () => evaluator.evaluate(condition, context);
21964
+ return {
21965
+ type: "until",
21966
+ condition: check,
21967
+ indexVariable,
21968
+ commands,
21969
+ elseCommands,
21970
+ bottomTested
21971
+ };
21527
21972
  }
21528
21973
  if (loopType === "forever") {
21529
21974
  return { type: "forever", indexVariable, commands, elseCommands };
@@ -21541,10 +21986,10 @@ var RepeatCommand = (() => {
21541
21986
  ({ config: config2, iterCtx } = createTimesLoopConfig(count, indexVariable));
21542
21987
  break;
21543
21988
  case "while":
21544
- ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateCondition, context, indexVariable));
21989
+ ({ config: config2, iterCtx } = createWhileLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
21545
21990
  break;
21546
21991
  case "until":
21547
- ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateCondition, context, indexVariable));
21992
+ ({ config: config2, iterCtx } = createUntilLoopConfig(condition, evaluateLoopCondition, context, indexVariable));
21548
21993
  break;
21549
21994
  case "until-event":
21550
21995
  ({ config: config2, iterCtx } = createUntilEventLoopConfig(eventName, eventTarget, indexVariable));
@@ -22526,6 +22971,29 @@ function namedPropertyOf(node) {
22526
22971
  }
22527
22972
  return null;
22528
22973
  }
22974
+ var RESTORE_KEYWORDS = /* @__PURE__ */ new Set(["initial", "inherit", "unset", "revert"]);
22975
+ function elementsOf(value) {
22976
+ if (Array.isArray(value) || isNodeList(value)) {
22977
+ return Array.from(value).filter(isHTMLElement$2);
22978
+ }
22979
+ return isHTMLElement$2(value) ? [value] : [];
22980
+ }
22981
+ async function valueOf(node, evaluator, context) {
22982
+ const value = await evaluator.evaluate(node, context);
22983
+ const name = node.name;
22984
+ if (value === void 0 && node.type === "identifier" && typeof name === "string")
22985
+ return name;
22986
+ return value;
22987
+ }
22988
+ function longestTransitionMs(css) {
22989
+ let longest;
22990
+ for (const item of css.split(",")) {
22991
+ const times = [...item.matchAll(/(\d*\.?\d+)(ms|s)\b/g)].map((m) => parseFloat(m[1]) * (m[2] === "s" ? 1e3 : 1));
22992
+ if (times.length > 0)
22993
+ longest = Math.max(longest ?? 0, times[0] + (times[1] ?? 0));
22994
+ }
22995
+ return longest;
22996
+ }
22529
22997
  var TransitionCommand = (() => {
22530
22998
  let _classDecorators = [command({ name: "transition" })];
22531
22999
  let _classDescriptor;
@@ -22541,10 +23009,9 @@ var TransitionCommand = (() => {
22541
23009
  let property;
22542
23010
  let target;
22543
23011
  const firstArg = await evaluator.evaluate(raw.args[0], context);
22544
- const asElement = (value2) => Array.isArray(value2) || isNodeList(value2) ? value2[0] : value2;
22545
- const firstAsElement = asElement(firstArg);
22546
- if (isHTMLElement$2(firstAsElement) || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
22547
- target = firstAsElement;
23012
+ const owners = elementsOf(firstArg);
23013
+ if (owners.length > 0 || typeof firstArg === "string" && /^[#.]|^(?:me|it|you)$/.test(firstArg)) {
23014
+ target = owners.length === 1 ? owners[0] : owners.length > 1 ? owners : firstArg;
22548
23015
  property = String(await evaluator.evaluate(raw.args[1], context));
22549
23016
  } else if (raw.args.length >= 2) {
22550
23017
  throw new Error("transition: target element not found");
@@ -22554,9 +23021,9 @@ var TransitionCommand = (() => {
22554
23021
  throw new Error("transition requires a CSS property");
22555
23022
  property = named.property;
22556
23023
  if (named.objectNode !== void 0) {
22557
- const owner = asElement(await evaluator.evaluate(named.objectNode, context));
22558
- if (isHTMLElement$2(owner))
22559
- target = owner;
23024
+ const owner = elementsOf(await evaluator.evaluate(named.objectNode, context));
23025
+ if (owner.length > 0)
23026
+ target = owner.length === 1 ? owner[0] : owner;
22560
23027
  }
22561
23028
  } else {
22562
23029
  property = String(firstArg);
@@ -22566,55 +23033,126 @@ var TransitionCommand = (() => {
22566
23033
  }
22567
23034
  if (!raw.modifiers?.to)
22568
23035
  throw new Error('transition requires "to <value>"');
22569
- let value = await evaluator.evaluate(raw.modifiers.to, context);
22570
- if (value === void 0 && raw.modifiers.to.type === "identifier") {
22571
- value = raw.modifiers.to.name;
22572
- }
22573
- const result = { property, value };
23036
+ const result = {
23037
+ property,
23038
+ value: await valueOf(raw.modifiers.to, evaluator, context)
23039
+ };
22574
23040
  if (target !== void 0)
22575
23041
  result.target = target;
23042
+ if (raw.modifiers?.from)
23043
+ result.from = await valueOf(raw.modifiers.from, evaluator, context);
23044
+ if (raw.modifiers?.pairs) {
23045
+ result.pairs = await this.laterPairs(raw.modifiers.pairs, evaluator, context);
23046
+ }
22576
23047
  if (raw.modifiers?.over)
22577
23048
  result.duration = await evaluator.evaluate(raw.modifiers.over, context);
22578
23049
  if (raw.modifiers?.with)
22579
23050
  result.timingFunction = String(await evaluator.evaluate(raw.modifiers.with, context));
23051
+ if (raw.modifiers?.using)
23052
+ result.using = String(await evaluator.evaluate(raw.modifiers.using, context));
22580
23053
  return result;
22581
23054
  }
23055
+ async laterPairs(node, evaluator, context) {
23056
+ const pairs = [];
23057
+ for (const pairNode of node.elements ?? []) {
23058
+ const fields = /* @__PURE__ */ new Map();
23059
+ const properties = pairNode.properties ?? [];
23060
+ for (const { key, value } of properties) {
23061
+ if (typeof key.name === "string")
23062
+ fields.set(key.name, value);
23063
+ }
23064
+ const propertyNode2 = fields.get("property");
23065
+ const toNode = fields.get("to");
23066
+ if (!propertyNode2 || !toNode)
23067
+ throw new Error("transition requires property and value");
23068
+ const pair = {
23069
+ property: String(await evaluator.evaluate(propertyNode2, context)),
23070
+ value: await valueOf(toNode, evaluator, context)
23071
+ };
23072
+ const ownerNode = fields.get("owner");
23073
+ if (ownerNode) {
23074
+ const owners = elementsOf(await evaluator.evaluate(ownerNode, context));
23075
+ if (owners.length === 0)
23076
+ throw new Error("transition: target element not found");
23077
+ pair.target = owners.length === 1 ? owners[0] : owners;
23078
+ }
23079
+ const fromNode = fields.get("from");
23080
+ if (fromNode)
23081
+ pair.from = await valueOf(fromNode, evaluator, context);
23082
+ pairs.push(pair);
23083
+ }
23084
+ return pairs;
23085
+ }
22582
23086
  async execute(input, context) {
22583
- let { property } = input;
22584
- const { target, value, duration: durationInput, timingFunction } = input;
22585
- if (property.startsWith("*"))
22586
- property = property.substring(1);
22587
- property = camelToKebab(property);
22588
- const targetElement = resolveElement(target, context, "transition");
22589
- const duration = parseDuration(durationInput, 300);
22590
- const fromValue = getComputedStyle(targetElement).getPropertyValue(property);
22591
- const originalTransition = targetElement.style.transition;
22592
- const transitionProp = `${property} ${duration}ms ${timingFunction || "ease"}`;
22593
- targetElement.style.transition = originalTransition ? `${originalTransition}, ${transitionProp}` : transitionProp;
22594
- let toValue = String(value);
22595
- let removeInlineAfter = false;
22596
- if (toValue === "initial" || toValue === "inherit" || toValue === "unset" || toValue === "revert") {
22597
- const currentInline = targetElement.style.getPropertyValue(property);
22598
- targetElement.style.removeProperty(property);
22599
- toValue = getComputedStyle(targetElement).getPropertyValue(property);
22600
- if (currentInline) {
22601
- targetElement.style.setProperty(property, currentInline);
22602
- }
22603
- removeInlineAfter = true;
22604
- }
22605
- targetElement.style.setProperty(property, toValue);
22606
- const result = await waitForTransitionEnd(targetElement, property, duration);
22607
- targetElement.style.transition = originalTransition;
22608
- if (removeInlineAfter) {
22609
- targetElement.style.removeProperty(property);
23087
+ const duration = parseDuration(input.duration, input.using !== void 0 && longestTransitionMs(input.using) || 300);
23088
+ const timing = input.timingFunction || "ease";
23089
+ const jobs = [];
23090
+ for (const pair of [input, ...input.pairs ?? []]) {
23091
+ let property = pair.property;
23092
+ if (property.startsWith("*"))
23093
+ property = property.substring(1);
23094
+ property = camelToKebab(property);
23095
+ const elements = Array.isArray(pair.target) ? pair.target.filter(isHTMLElement$2) : [resolveElement(pair.target, context, "transition")];
23096
+ for (const element of elements) {
23097
+ jobs.push({
23098
+ element,
23099
+ property,
23100
+ pair,
23101
+ fromValue: getComputedStyle(element).getPropertyValue(property),
23102
+ toValue: "",
23103
+ removeInlineAfter: false
23104
+ });
23105
+ }
23106
+ }
23107
+ if (jobs.length === 0)
23108
+ throw new Error("transition: target element not found");
23109
+ for (const job of jobs) {
23110
+ if (job.pair.from !== void 0) {
23111
+ job.element.style.setProperty(job.property, String(job.pair.from));
23112
+ }
23113
+ }
23114
+ for (const job of jobs)
23115
+ if (job.pair.from !== void 0)
23116
+ void job.element.offsetWidth;
23117
+ const originals = /* @__PURE__ */ new Map();
23118
+ for (const job of jobs) {
23119
+ if (!originals.has(job.element))
23120
+ originals.set(job.element, job.element.style.transition);
23121
+ }
23122
+ for (const [element, original] of originals) {
23123
+ const own = jobs.filter((job) => job.element === element).map((job) => `${job.property} ${duration}ms ${timing}`);
23124
+ element.style.transition = input.using ?? [original, ...own].filter(Boolean).join(", ");
23125
+ }
23126
+ for (const job of jobs) {
23127
+ const { element, property } = job;
23128
+ let toValue = String(job.pair.value);
23129
+ if (RESTORE_KEYWORDS.has(toValue)) {
23130
+ const currentInline = element.style.getPropertyValue(property);
23131
+ element.style.removeProperty(property);
23132
+ toValue = getComputedStyle(element).getPropertyValue(property);
23133
+ if (currentInline) {
23134
+ element.style.setProperty(property, currentInline);
23135
+ }
23136
+ job.removeInlineAfter = true;
23137
+ }
23138
+ job.toValue = toValue;
23139
+ element.style.setProperty(property, toValue);
22610
23140
  }
23141
+ const results = await Promise.all(jobs.map((job) => waitForTransitionEnd(job.element, job.property, duration)));
23142
+ for (const [element, original] of originals)
23143
+ element.style.transition = original;
23144
+ for (const job of jobs) {
23145
+ if (job.removeInlineAfter)
23146
+ job.element.style.removeProperty(job.property);
23147
+ }
23148
+ const first = jobs[0];
22611
23149
  return {
22612
- element: targetElement,
22613
- property,
22614
- fromValue,
22615
- toValue,
23150
+ element: first.element,
23151
+ property: first.property,
23152
+ fromValue: first.fromValue,
23153
+ toValue: first.toValue,
22616
23154
  duration,
22617
- completed: result.completed
23155
+ completed: results.every((result) => result.completed)
22618
23156
  };
22619
23157
  }
22620
23158
  };
@@ -22627,13 +23165,16 @@ var TransitionCommand = (() => {
22627
23165
  })();
22628
23166
  _classThis.metadata = commandMeta({
22629
23167
  description: "Animate CSS properties using CSS transitions",
22630
- syntax: "transition [<target>] <property> to <value> [over <duration>] [with <timing>]",
23168
+ syntax: "transition <property> [from <value>] to <value> [<property> \u2026 to <value>]\u2026 [over <duration>] [using <css> | with <timing>]",
22631
23169
  examples: [
22632
23170
  "transition opacity to 0.5",
22633
23171
  "transition my *opacity to 0 over 200ms",
22634
23172
  "transition #box's *opacity to 0 over 200ms",
22635
23173
  "transition left to 100px over 500ms",
22636
- "transition background-color to red over 1s with ease-in-out"
23174
+ "transition background-color to red over 1s with ease-in-out",
23175
+ "transition *width to 100px *height to 50px over 300ms",
23176
+ "transition *opacity of #panel from 0 to 1 over 200ms",
23177
+ 'transition my *opacity to 0 using "opacity 1s ease-in"'
22637
23178
  ],
22638
23179
  sideEffects: ["style-change", "timing"],
22639
23180
  category: "animation",
@@ -23834,17 +24375,7 @@ var BeepCommand = (() => {
23834
24375
  this.debugContext(context);
23835
24376
  return { expressionCount: 0, debugged: true, outputs: [] };
23836
24377
  }
23837
- const outputs = [];
23838
- console.group("\u{1F514} beep! Debug Output");
23839
- for (const expression of expressions) {
23840
- const output = this.debugExpression(expression);
23841
- outputs.push(output);
23842
- console.log(`Value:`, expression);
23843
- console.log(`Type:`, output.type);
23844
- console.log(`Representation:`, output.representation);
23845
- console.log("---");
23846
- }
23847
- console.groupEnd();
24378
+ const outputs = beepValues(context.me, expressions);
23848
24379
  return { expressionCount: expressions.length, debugged: true, outputs };
23849
24380
  }
23850
24381
  debugContext(context) {
@@ -23857,63 +24388,6 @@ var BeepCommand = (() => {
23857
24388
  console.log("variables:", context.variables);
23858
24389
  console.groupEnd();
23859
24390
  }
23860
- debugExpression(expression) {
23861
- return {
23862
- value: expression,
23863
- type: this.getType(expression),
23864
- representation: this.getRepresentation(expression)
23865
- };
23866
- }
23867
- getType(value) {
23868
- if (value === null)
23869
- return "null";
23870
- if (value === void 0)
23871
- return "undefined";
23872
- if (Array.isArray(value))
23873
- return "array";
23874
- if (isHTMLElement$2(value))
23875
- return "HTMLElement";
23876
- if (value instanceof Element)
23877
- return "Element";
23878
- if (value instanceof Node)
23879
- return "Node";
23880
- if (value instanceof Error)
23881
- return "Error";
23882
- if (value instanceof Date)
23883
- return "Date";
23884
- if (value instanceof RegExp)
23885
- return "RegExp";
23886
- return typeof value;
23887
- }
23888
- getRepresentation(value) {
23889
- if (value === null)
23890
- return "null";
23891
- if (value === void 0)
23892
- return "undefined";
23893
- if (Array.isArray(value)) {
23894
- return `Array(${value.length}) [${value.slice(0, 3).map((v2) => this.getRepresentation(v2)).join(", ")}${value.length > 3 ? "..." : ""}]`;
23895
- }
23896
- if (isHTMLElement$2(value)) {
23897
- const el = value;
23898
- const tag = el.tagName.toLowerCase();
23899
- const id = el.id ? `#${el.id}` : "";
23900
- const classes = el.className ? `.${el.className.split(" ").join(".")}` : "";
23901
- return `<${tag}${id}${classes}/>`;
23902
- }
23903
- if (value instanceof Error)
23904
- return `Error: ${value.message}`;
23905
- if (typeof value === "string")
23906
- return value.length > 50 ? `"${value.substring(0, 47)}..."` : `"${value}"`;
23907
- if (typeof value === "object") {
23908
- try {
23909
- const keys = Object.keys(value);
23910
- return `Object {${keys.slice(0, 3).join(", ")}${keys.length > 3 ? "..." : ""}}`;
23911
- } catch {
23912
- return "[Object]";
23913
- }
23914
- }
23915
- return String(value);
23916
- }
23917
24391
  };
23918
24392
  __setFunctionName(_classThis, "BeepCommand");
23919
24393
  (() => {
@@ -24437,16 +24911,12 @@ var RenderCommand = class _RenderCommand {
24437
24911
  };
24438
24912
  RenderCommand.metadata = commandMeta({
24439
24913
  description: "Render templates with @if, @else, and @repeat directives",
24440
- syntax: [
24441
- "render <template>",
24442
- "render <template> with <variables>",
24443
- "render <template> with (key: value, ...)"
24444
- ],
24914
+ syntax: ["render <template>", "render <template> with <name>: <value>, ..."],
24445
24915
  examples: [
24446
24916
  "render myTemplate",
24447
- 'render myTemplate with (name: "Alice")',
24448
- 'render "<template>Hello ${name}!</template>" with (name: "World")',
24449
- "render template with (items: data)"
24917
+ 'render myTemplate with name: "Alice"',
24918
+ 'render "<template>Hello ${name}!</template>" with name: "World"',
24919
+ "render template with items: data"
24450
24920
  ],
24451
24921
  category: "templates",
24452
24922
  compatibility: "standard",
@@ -24617,7 +25087,7 @@ function updateDebugStats(event) {
24617
25087
  }
24618
25088
  stats.averageConfidence = stats.confidenceHistory.reduce((a, b) => a + b, 0) / stats.confidenceHistory.length;
24619
25089
  }
24620
- var VERSION = "3.1.1";
25090
+ var VERSION = "3.3.0";
24621
25091
  function createHistorySwap(config2) {
24622
25092
  const { target, strategy = "morph", useViewTransition = false, fetchOptions = {}, transformUrl, onBeforeFetch, onAfterSwap, onError } = config2;
24623
25093
  const resolveTarget = () => {
@@ -26125,6 +26595,14 @@ function convertNode(node, infer) {
26125
26595
  ...pos(node)
26126
26596
  };
26127
26597
  case "identifier":
26598
+ if (node.scope === "element" || node.scope === "local" || node.scope === "global") {
26599
+ return {
26600
+ type: "variable",
26601
+ name: node.name ?? "",
26602
+ scope: node.scope,
26603
+ ...pos(node)
26604
+ };
26605
+ }
26128
26606
  return {
26129
26607
  type: "identifier",
26130
26608
  value: node.name ?? node.value ?? "",
@@ -26147,6 +26625,15 @@ function convertNode(node, infer) {
26147
26625
  operand: convertNode(node.argument ?? node.operand, infer),
26148
26626
  ...pos(node)
26149
26627
  };
26628
+ case "typeCheckExpression":
26629
+ return {
26630
+ type: "typeCheck",
26631
+ value: convertNode(node.value, infer),
26632
+ typeName: String(node.typeName),
26633
+ nullOk: node.nullOk !== false,
26634
+ negated: node.negated === true,
26635
+ ...pos(node)
26636
+ };
26150
26637
  case "timeExpression":
26151
26638
  return { type: "literal", value: node.value, ...pos(node) };
26152
26639
  case "templateLiteral":
@@ -26158,6 +26645,28 @@ function convertNode(node, infer) {
26158
26645
  scope: node.scope ?? "local",
26159
26646
  ...pos(node)
26160
26647
  };
26648
+ case "arrayLiteral":
26649
+ return {
26650
+ type: "array",
26651
+ elements: (node.elements ?? []).map((element) => convertNode(element, infer)),
26652
+ ...pos(node)
26653
+ };
26654
+ case "attributeAccess":
26655
+ return {
26656
+ type: "possessive",
26657
+ object: { type: "identifier", value: "me" },
26658
+ property: `@${node.attributeName}`,
26659
+ ...pos(node)
26660
+ };
26661
+ case "propertyOfExpression": {
26662
+ const property = node.property;
26663
+ return {
26664
+ type: "possessive",
26665
+ object: convertNode(node.target, infer),
26666
+ property: property?.name ?? property?.value ?? "",
26667
+ ...pos(node)
26668
+ };
26669
+ }
26161
26670
  case "htmlSelector":
26162
26671
  return {
26163
26672
  type: "selector",
@@ -26208,6 +26717,18 @@ function convertCommand$1(node, infer) {
26208
26717
  if (name === "repeat") {
26209
26718
  return convertRepeatCommand(node, infer);
26210
26719
  }
26720
+ const original = node.originalCommand;
26721
+ const sum = node.modifiers?.to;
26722
+ const counted = node.args?.[0];
26723
+ if (name === "set" && (original === "increment" || original === "decrement") && sum?.type === "binaryExpression" && counted) {
26724
+ return convertCommand$1({
26725
+ ...node,
26726
+ name: original,
26727
+ args: [counted],
26728
+ modifiers: { by: sum.right },
26729
+ originalCommand: void 0
26730
+ }, infer);
26731
+ }
26211
26732
  const args = (node.args ?? []).map((arg) => convertNode(arg, infer));
26212
26733
  const target = node.target ? convertNode(node.target, infer) : void 0;
26213
26734
  const modifiers = node.modifiers ? convertModifiers(node.modifiers, infer) : void 0;
@@ -26249,6 +26770,64 @@ function convertIfCommand(node, infer) {
26249
26770
  };
26250
26771
  }
26251
26772
  function convertRepeatCommand(node, infer) {
26773
+ const slots = node.modifiers ?? {};
26774
+ const form = slotText(slots.loopType);
26775
+ if (form === void 0)
26776
+ return convertPositionalRepeat(node, infer);
26777
+ const [bodyBlock, elseBlock] = node.args ?? [];
26778
+ const body = extractBlockCommands(bodyBlock, infer);
26779
+ const indexName = slotText(slots.index);
26780
+ const shared = {
26781
+ ...indexName ? { indexName } : {},
26782
+ ...elseBlock ? { elseBody: extractBlockCommands(elseBlock, infer) } : {},
26783
+ ...pos(node)
26784
+ };
26785
+ const operand = (slot) => slot ? convertNode(slot, infer) : void 0;
26786
+ switch (form) {
26787
+ case "for":
26788
+ return {
26789
+ type: "foreach",
26790
+ itemName: slotText(slots.for) ?? "it",
26791
+ collection: operand(slots.in) ?? { type: "literal", value: null },
26792
+ body,
26793
+ ...shared
26794
+ };
26795
+ case "times":
26796
+ return { type: "repeat", count: operand(slots.times), body, ...shared };
26797
+ case "while":
26798
+ case "until": {
26799
+ const test = operand(slots[form]) ?? { type: "literal", value: form === "until" };
26800
+ return {
26801
+ type: "while",
26802
+ condition: form === "until" ? { type: "unary", operator: "not", operand: test } : test,
26803
+ ...slots.bottomTested?.value === true ? { bottomTested: true } : {},
26804
+ body,
26805
+ ...shared
26806
+ };
26807
+ }
26808
+ case "until-event": {
26809
+ const event = slotText(slots.event);
26810
+ if (!event) {
26811
+ return { type: "error", message: "'repeat until event' has no event name", ...pos(node) };
26812
+ }
26813
+ const target = operand(slots.from);
26814
+ return {
26815
+ type: "repeat",
26816
+ untilEvent: event,
26817
+ ...target ? { untilEventTarget: target } : {},
26818
+ body,
26819
+ ...shared
26820
+ };
26821
+ }
26822
+ default:
26823
+ return { type: "repeat", body, ...shared };
26824
+ }
26825
+ }
26826
+ function slotText(slot) {
26827
+ const text = slot?.value ?? slot?.name;
26828
+ return typeof text === "string" ? text : void 0;
26829
+ }
26830
+ function convertPositionalRepeat(node, infer) {
26252
26831
  const args = node.args ?? [];
26253
26832
  if (args.length === 0) {
26254
26833
  return { type: "repeat", body: [], ...pos(node) };
@@ -26368,13 +26947,13 @@ function buildEventModifiers(node) {
26368
26947
  ...node.selector && !node.from ? { from: node.selector } : {}
26369
26948
  };
26370
26949
  }
26371
- function extractBlockCommands(block, infer) {
26372
- if (!block)
26950
+ function extractBlockCommands(block2, infer) {
26951
+ if (!block2)
26373
26952
  return [];
26374
- if (block.type === "block") {
26375
- return (block.commands ?? []).map((cmd) => convertNode(cmd, infer));
26953
+ if (block2.type === "block") {
26954
+ return (block2.commands ?? []).map((cmd) => convertNode(cmd, infer));
26376
26955
  }
26377
- return [convertNode(block, infer)];
26956
+ return [convertNode(block2, infer)];
26378
26957
  }
26379
26958
  function inferRoles(name, args, modifiers, target, infer) {
26380
26959
  const roles = {};
@@ -26535,6 +27114,15 @@ function toCoreAST(node) {
26535
27114
  prefix: true,
26536
27115
  ...nodePos(node)
26537
27116
  };
27117
+ case "typeCheck":
27118
+ return {
27119
+ type: "typeCheckExpression",
27120
+ value: toCoreAST(node.value),
27121
+ typeName: node.typeName,
27122
+ nullOk: node.nullOk,
27123
+ negated: node.negated,
27124
+ ...nodePos(node)
27125
+ };
26538
27126
  case "member":
26539
27127
  return {
26540
27128
  type: "memberExpression",
@@ -26543,6 +27131,12 @@ function toCoreAST(node) {
26543
27131
  computed: node.computed ?? false,
26544
27132
  ...nodePos(node)
26545
27133
  };
27134
+ case "array":
27135
+ return {
27136
+ type: "arrayLiteral",
27137
+ elements: node.elements.map((element) => toCoreAST(element)),
27138
+ ...nodePos(node)
27139
+ };
26546
27140
  case "possessive":
26547
27141
  return {
26548
27142
  type: "possessiveExpression",
@@ -26602,67 +27196,79 @@ function convertCommand(node) {
26602
27196
  }
26603
27197
  return result;
26604
27198
  }
27199
+ function block(nodes) {
27200
+ return { type: "block", commands: nodes.map((n) => toCoreAST(n)), ...POS };
27201
+ }
26605
27202
  function convertIf(node) {
26606
- const result = {
27203
+ const [elseIf, ...laterElseIfs] = node.elseIfBranches ?? [];
27204
+ const elseBranch = elseIf ? [
27205
+ {
27206
+ type: "if",
27207
+ condition: elseIf.condition,
27208
+ thenBranch: elseIf.body,
27209
+ elseIfBranches: laterElseIfs,
27210
+ ...node.elseBranch ? { elseBranch: node.elseBranch } : {}
27211
+ }
27212
+ ] : node.elseBranch;
27213
+ return {
26607
27214
  type: "command",
26608
27215
  name: "if",
26609
27216
  isBlocking: true,
26610
- condition: toCoreAST(node.condition),
26611
- thenBranch: node.thenBranch.map((n) => toCoreAST(n)),
26612
- args: [],
27217
+ args: [
27218
+ toCoreAST(node.condition),
27219
+ block(node.thenBranch),
27220
+ ...elseBranch ? [block(elseBranch)] : []
27221
+ ],
26613
27222
  ...nodePos(node)
26614
27223
  };
26615
- if (node.elseBranch) {
26616
- result.elseBranch = node.elseBranch.map((n) => toCoreAST(n));
26617
- }
26618
- return result;
26619
27224
  }
26620
- function convertRepeat(node) {
26621
- const result = {
27225
+ function loopCommand(node, form, operands) {
27226
+ const modifiers = { loopType: slotString(form) };
27227
+ for (const [slot, value] of Object.entries(operands)) {
27228
+ if (value)
27229
+ modifiers[slot] = value;
27230
+ }
27231
+ if (node.indexName)
27232
+ modifiers.index = slotString(node.indexName);
27233
+ return {
26622
27234
  type: "command",
26623
27235
  name: "repeat",
26624
27236
  isBlocking: true,
26625
- body: node.body.map((n) => toCoreAST(n)),
26626
- args: [],
27237
+ args: [block(node.body), ...node.elseBody ? [block(node.elseBody)] : []],
27238
+ modifiers,
26627
27239
  ...nodePos(node)
26628
27240
  };
27241
+ }
27242
+ function slotString(value) {
27243
+ return { type: "string", value, ...POS };
27244
+ }
27245
+ function convertRepeat(node) {
26629
27246
  if (node.count !== void 0) {
26630
- result.count = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
26631
- result.loopVariant = "times";
26632
- } else if (node.whileCondition !== void 0) {
26633
- result.condition = toCoreAST(node.whileCondition);
26634
- result.loopVariant = "while";
26635
- } else if (node.untilEvent !== void 0) {
26636
- result.untilEvent = node.untilEvent;
26637
- result.loopVariant = "until";
27247
+ const times = typeof node.count === "number" ? { type: "literal", value: node.count, raw: String(node.count), ...POS } : toCoreAST(node.count);
27248
+ return loopCommand(node, "times", { times });
26638
27249
  }
26639
- return result;
27250
+ if (node.whileCondition !== void 0) {
27251
+ return loopCommand(node, "while", { while: toCoreAST(node.whileCondition) });
27252
+ }
27253
+ if (node.untilEvent !== void 0) {
27254
+ return loopCommand(node, "until-event", {
27255
+ event: slotString(node.untilEvent),
27256
+ from: node.untilEventTarget ? toCoreAST(node.untilEventTarget) : void 0
27257
+ });
27258
+ }
27259
+ return loopCommand(node, "forever", {});
26640
27260
  }
26641
27261
  function convertForEach(node) {
26642
- return {
26643
- type: "command",
26644
- name: "repeat",
26645
- isBlocking: true,
26646
- loopVariant: "for",
26647
- itemName: node.itemName,
26648
- ...node.indexName ? { indexName: node.indexName } : {},
26649
- collection: toCoreAST(node.collection),
26650
- body: node.body.map((n) => toCoreAST(n)),
26651
- args: [],
26652
- ...nodePos(node)
26653
- };
27262
+ return loopCommand(node, "for", {
27263
+ for: slotString(node.itemName),
27264
+ in: toCoreAST(node.collection)
27265
+ });
26654
27266
  }
26655
27267
  function convertWhile(node) {
26656
- return {
26657
- type: "command",
26658
- name: "repeat",
26659
- isBlocking: true,
26660
- loopVariant: "while",
26661
- condition: toCoreAST(node.condition),
26662
- body: node.body.map((n) => toCoreAST(n)),
26663
- args: [],
26664
- ...nodePos(node)
26665
- };
27268
+ return loopCommand(node, "while", {
27269
+ while: toCoreAST(node.condition),
27270
+ bottomTested: node.bottomTested ? { type: "literal", value: true, raw: "true", ...POS } : void 0
27271
+ });
26666
27272
  }
26667
27273
  function flattenEventModifiers(modifiers) {
26668
27274
  if (!modifiers)
@@ -27071,4 +27677,4 @@ export {
27071
27677
  toCoreAST,
27072
27678
  validatePartialContent
27073
27679
  };
27074
- //# sourceMappingURL=dist-HOHQFWDA.js.map
27680
+ //# sourceMappingURL=dist-OK2I5ODH.js.map