bare-script 4.2.2__tar.gz → 4.2.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. {bare_script-4.2.2/src/bare_script.egg-info → bare_script-4.2.4}/PKG-INFO +6 -5
  2. {bare_script-4.2.2 → bare_script-4.2.4}/README.md +5 -4
  3. {bare_script-4.2.2 → bare_script-4.2.4}/setup.cfg +1 -1
  4. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/dataTable.bare +35 -20
  5. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/library.py +22 -22
  6. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/value.py +6 -1
  7. {bare_script-4.2.2 → bare_script-4.2.4/src/bare_script.egg-info}/PKG-INFO +6 -5
  8. {bare_script-4.2.2 → bare_script-4.2.4}/LICENSE +0 -0
  9. {bare_script-4.2.2 → bare_script-4.2.4}/pyproject.toml +0 -0
  10. {bare_script-4.2.2 → bare_script-4.2.4}/setup.py +0 -0
  11. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/__init__.py +0 -0
  12. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/__main__.py +0 -0
  13. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/bare.py +0 -0
  14. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/__init__.py +0 -0
  15. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/args.bare +0 -0
  16. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/baredoc.bare +0 -0
  17. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/baredocCLI.bare +0 -0
  18. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/data.bare +0 -0
  19. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/dataLineChart.bare +0 -0
  20. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/dataUtil.bare +0 -0
  21. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/diff.bare +0 -0
  22. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/draw.bare +0 -0
  23. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/elementModel.bare +0 -0
  24. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/forms.bare +0 -0
  25. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/markdown.bare +0 -0
  26. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/markdownElements.bare +0 -0
  27. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/markdownHighlight.bare +0 -0
  28. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/markdownParser.bare +0 -0
  29. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/markdownUp.bare +0 -0
  30. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/pager.bare +0 -0
  31. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/qrcode.bare +0 -0
  32. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/schemaDoc.bare +0 -0
  33. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/unittest.bare +0 -0
  34. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/include/unittestMock.bare +0 -0
  35. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/model.py +0 -0
  36. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/options.py +0 -0
  37. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/parser.py +0 -0
  38. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/runtime.py +0 -0
  39. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script/runtime_c.c +0 -0
  40. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script.egg-info/SOURCES.txt +0 -0
  41. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script.egg-info/dependency_links.txt +0 -0
  42. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script.egg-info/entry_points.txt +0 -0
  43. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script.egg-info/requires.txt +0 -0
  44. {bare_script-4.2.2 → bare_script-4.2.4}/src/bare_script.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bare-script
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: bare-script
5
5
  Home-page: https://github.com/craigahobbs/bare-script
6
6
  Author: Craig A. Hobbs
@@ -219,17 +219,18 @@ cp SKILL.md .claude/skills/bare-script/SKILL.md
219
219
  Use `~/.claude/skills/bare-script/SKILL.md` instead to make it available across all projects. For
220
220
  other assistants, include the file's contents in your system prompt or rules file.
221
221
 
222
- Once installed, you can prompt the assistant with tasks like:
222
+ Once installed, prompt the assistant with a task like:
223
223
 
224
- > Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button
225
- > and a running win/loss/draw tally rendered as a bar chart.
224
+ ```
225
+ claude "Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button and a running win/loss/draw tally rendered as a bar chart. Save it as ticTacToe.md"
226
+ ```
226
227
 
227
228
  To run the resulting MarkdownUp application locally, install the
228
229
  [markdown-up](https://pypi.org/project/markdown-up/) viewer and point it at the Markdown file:
229
230
 
230
231
  ```
231
232
  pip install markdown-up
232
- markdown-up yourApp.md
233
+ markdown-up ticTacToe.md
233
234
  ```
234
235
 
235
236
  The BareScript library is also documented as single-page Markdown, which can be fetched directly
@@ -194,17 +194,18 @@ cp SKILL.md .claude/skills/bare-script/SKILL.md
194
194
  Use `~/.claude/skills/bare-script/SKILL.md` instead to make it available across all projects. For
195
195
  other assistants, include the file's contents in your system prompt or rules file.
196
196
 
197
- Once installed, you can prompt the assistant with tasks like:
197
+ Once installed, prompt the assistant with a task like:
198
198
 
199
- > Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button
200
- > and a running win/loss/draw tally rendered as a bar chart.
199
+ ```
200
+ claude "Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button and a running win/loss/draw tally rendered as a bar chart. Save it as ticTacToe.md"
201
+ ```
201
202
 
202
203
  To run the resulting MarkdownUp application locally, install the
203
204
  [markdown-up](https://pypi.org/project/markdown-up/) viewer and point it at the Markdown file:
204
205
 
205
206
  ```
206
207
  pip install markdown-up
207
- markdown-up yourApp.md
208
+ markdown-up ticTacToe.md
208
209
  ```
209
210
 
210
211
  The BareScript library is also documented as single-page Markdown, which can be fetched directly
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = bare-script
3
- version = 4.2.2
3
+ version = 4.2.4
4
4
  url = https://github.com/craigahobbs/bare-script
5
5
  author = Craig A. Hobbs
6
6
  author_email = craigahobbs@gmail.com
@@ -87,19 +87,26 @@ endfunction
87
87
  # $arg model: The [data table model](model.html#var.vName='DataTable')
88
88
  # $return: The array of Markdown table line strings
89
89
  function dataTableMarkdown(data, model):
90
- # Determine the table fields
90
+ # Determine the table fields - categories first, then the value fields
91
91
  fields = []
92
92
  modelFields = if(model != null, objectGet(model, 'fields'))
93
93
  modelCategories = if(model != null, objectGet(model, 'categories'))
94
- if modelFields != null || modelCategories != null:
95
- if modelCategories != null:
96
- arrayExtend(fields, modelCategories)
97
- endif
98
- if modelFields != null:
99
- arrayExtend(fields, modelFields)
100
- endif
101
- elif arrayLength(data) > 0:
102
- arrayExtend(fields, objectKeys(arrayGet(data, 0)))
94
+ if modelCategories != null:
95
+ arrayExtend(fields, modelCategories)
96
+ endif
97
+ if modelFields != null:
98
+ arrayExtend(fields, modelFields)
99
+ else:
100
+ # Compute the data's fields (union of all row keys), excluding category fields
101
+ fieldNames = {}
102
+ for row in data:
103
+ for field in objectKeys(row):
104
+ if modelCategories == null || arrayIndexOf(modelCategories, field) == -1:
105
+ objectSet(fieldNames, field, null)
106
+ endif
107
+ endfor
108
+ endfor
109
+ arrayExtend(fields, objectKeys(fieldNames))
103
110
  endif
104
111
  if !arrayLength(fields):
105
112
  return null
@@ -118,7 +125,6 @@ function dataTableMarkdown(data, model):
118
125
  endfor
119
126
 
120
127
  # Compute the formatted field value strings and widths
121
- hasDatetimeFormat = precisionDatetime != null
122
128
  dataFormat = []
123
129
  for row in data:
124
130
  rowFormat = {}
@@ -127,10 +133,8 @@ function dataTableMarkdown(data, model):
127
133
  # Format the value (newline regex only on string/other types - numbers and datetimes can't contain newlines)
128
134
  value = objectGet(row, field)
129
135
  valueType = systemType(value)
130
- if valueType == 'number':
131
- valueFormat = numberToFixed(value, precisionNumber, precisionTrim)
132
- elif valueType == 'datetime':
133
- valueFormat = datetimeISOFormat(value, hasDatetimeFormat)
136
+ if valueType == 'number' || valueType == 'datetime':
137
+ valueFormat = dataUtilFormatValue(value, precisionNumber, precisionDatetime, precisionTrim)
134
138
  else:
135
139
  valueFormat = if(valueType == 'string', stringTrim(value), stringNew(value))
136
140
  valueFormat = regexReplace(dataTableMarkdownRegexMultipleNewline, valueFormat, '<br><br>')
@@ -148,7 +152,7 @@ function dataTableMarkdown(data, model):
148
152
  endfor
149
153
  endfor
150
154
 
151
- # Precompute per-field rendering metadata: [field, width, align, header]
155
+ # Precompute per-field rendering metadata: [field, width, align, header, isCategory]
152
156
  fieldMeta = []
153
157
  for field in fields:
154
158
  format = if(formats != null, objectGet(formats, field))
@@ -156,7 +160,8 @@ function dataTableMarkdown(data, model):
156
160
  field, \
157
161
  objectGet(widths, field), \
158
162
  if(format != null, objectGet(format, 'align')), \
159
- if(format != null, objectGet(format, 'header', field), field) \
163
+ if(format != null, objectGet(format, 'header', field), field), \
164
+ modelCategories != null && arrayIndexOf(modelCategories, field) != -1 \
160
165
  ])
161
166
  endfor
162
167
 
@@ -186,14 +191,24 @@ function dataTableMarkdown(data, model):
186
191
  arrayPush(lines, headerFields)
187
192
  arrayPush(lines, headerSeparator)
188
193
 
189
- # Output each row
190
- for row in dataFormat:
194
+ # Output each row, blanking duplicate leading category cell values
195
+ for row, ixRow in dataFormat:
196
+ rowRaw = arrayGet(data, ixRow)
197
+ rowPrevRaw = if(ixRow > 0, arrayGet(data, ixRow - 1))
198
+ skip = rowPrevRaw != null
191
199
  parts = []
192
200
  for meta in fieldMeta:
193
201
  field = arrayGet(meta, 0)
194
202
  width = arrayGet(meta, 1)
195
203
  align = arrayGet(meta, 2)
196
- arrayPush(parts, dataTableMarkdownField(objectGet(row, field), width, align, ' '))
204
+
205
+ # Skip this value? (blank a category cell matching the previous row)
206
+ if skip:
207
+ isCategory = arrayGet(meta, 4)
208
+ skip = isCategory && systemCompare(objectGet(rowRaw, field), objectGet(rowPrevRaw, field)) == 0
209
+ endif
210
+
211
+ arrayPush(parts, dataTableMarkdownField(if(skip, '', objectGet(row, field)), width, align, ' '))
197
212
  endfor
198
213
  arrayPush(lines, '| ' + arrayJoin(parts, ' | ') + ' |')
199
214
  endfor
@@ -69,7 +69,7 @@ def _array_delete(args, unused_options):
69
69
  if index >= len(array):
70
70
  raise ValueArgsError('index', index)
71
71
 
72
- del array[int(index)]
72
+ del array[index]
73
73
 
74
74
  _ARRAY_DELETE_ARGS = value_args_model([
75
75
  {'name': 'array', 'type': 'array'},
@@ -132,7 +132,7 @@ def _array_get(args, unused_options):
132
132
  if index >= len(array):
133
133
  raise ValueArgsError('index', index)
134
134
 
135
- return array[int(index)]
135
+ return array[index]
136
136
 
137
137
  _ARRAY_GET_ARGS = value_args_model([
138
138
  {'name': 'array', 'type': 'array'},
@@ -154,11 +154,11 @@ def _array_index_of(args, options):
154
154
 
155
155
  # Value function?
156
156
  if value_type(value) == 'function':
157
- for ix in range(int(index), len(array)):
157
+ for ix in range(index, len(array)):
158
158
  if value_boolean(value([array[ix]], options)):
159
159
  return ix
160
160
  else:
161
- for ix in range(int(index), len(array)):
161
+ for ix in range(index, len(array)):
162
162
  if value_compare(array[ix], value) == 0:
163
163
  return ix
164
164
 
@@ -203,11 +203,11 @@ def _array_last_index_of(args, options):
203
203
 
204
204
  # Value function?
205
205
  if value_type(value) == 'function':
206
- for ix in range(int(index), -1, -1):
206
+ for ix in range(index, -1, -1):
207
207
  if value_boolean(value([array[ix]], options)):
208
208
  return ix
209
209
  else:
210
- for ix in range(int(index), -1, -1):
210
+ for ix in range(index, -1, -1):
211
211
  if value_compare(array[ix], value) == 0:
212
212
  return ix
213
213
 
@@ -251,7 +251,7 @@ def _array_new(args, unused_options):
251
251
  # $return: The new array
252
252
  def _array_new_size(args, unused_options):
253
253
  size, value = value_args_validate(_ARRAY_NEW_SIZE_ARGS, args)
254
- return list(value for _ in range(int(size)))
254
+ return list(value for _ in range(size))
255
255
 
256
256
  _ARRAY_NEW_SIZE_ARGS = value_args_model([
257
257
  {'name': 'size', 'type': 'number', 'default': 0, 'integer': True, 'gte': 0},
@@ -320,7 +320,7 @@ def _array_set(args, unused_options):
320
320
  if index >= len(array):
321
321
  raise ValueArgsError('index', index)
322
322
 
323
- array[int(index)] = value
323
+ array[index] = value
324
324
  return value
325
325
 
326
326
  _ARRAY_SET_ARGS = value_args_model([
@@ -365,7 +365,7 @@ def _array_slice(args, unused_options):
365
365
  if end > len(array):
366
366
  raise ValueArgsError('end', end)
367
367
 
368
- return array[int(start):int(end)]
368
+ return array[start:end]
369
369
 
370
370
  _ARRAY_SLICE_ARGS = value_args_model([
371
371
  {'name': 'array', 'type': 'array'},
@@ -594,18 +594,18 @@ def _datetime_new(args, unused_options):
594
594
  while day < 1:
595
595
  year = year if month != 1 else year - 1
596
596
  month = month - 1 if month != 1 else 12
597
- _, month_days = calendar.monthrange(int(year), int(month))
597
+ _, month_days = calendar.monthrange(year, month)
598
598
  day += month_days
599
599
  elif day > 28:
600
- _, month_days = calendar.monthrange(int(year), int(month))
600
+ _, month_days = calendar.monthrange(year, month)
601
601
  while day > month_days:
602
602
  day -= month_days
603
603
  year = year if month != 12 else year + 1
604
604
  month = month + 1 if month != 12 else 1
605
- _, month_days = calendar.monthrange(int(year), int(month))
605
+ _, month_days = calendar.monthrange(year, month)
606
606
 
607
607
  # Return the datetime
608
- return datetime.datetime(int(year), int(month), int(day), int(hour), int(minute), int(second), int(millisecond) * 1000)
608
+ return datetime.datetime(year, month, day, hour, minute, second, millisecond * 1000)
609
609
 
610
610
  _DATETIME_NEW_ARGS = value_args_model([
611
611
  {'name': 'year', 'type': 'number', 'integer': True, 'gte': 100},
@@ -690,7 +690,7 @@ _JSON_PARSE_ARGS = value_args_model([
690
690
  # $return: The JSON string
691
691
  def _json_stringify(args, unused_options):
692
692
  value, indent = value_args_validate(_JSON_STRINGIFY_ARGS, args)
693
- return value_json(value, int(indent) if indent is not None else None)
693
+ return value_json(value, indent)
694
694
 
695
695
  _JSON_STRINGIFY_ARGS = value_args_model([
696
696
  {'name': 'value'},
@@ -1007,7 +1007,7 @@ _NUMBER_PARSE_FLOAT_ARGS = value_args_model([
1007
1007
  # $return: The integer
1008
1008
  def _number_parse_int(args, unused_options):
1009
1009
  string, radix = value_args_validate(_NUMBER_PARSE_INT_ARGS, args)
1010
- return value_parse_integer(string, int(radix))
1010
+ return value_parse_integer(string, radix)
1011
1011
 
1012
1012
  _NUMBER_PARSE_INT_ARGS = value_args_model([
1013
1013
  {'name': 'string', 'type': 'string'},
@@ -1024,7 +1024,7 @@ _NUMBER_PARSE_INT_ARGS = value_args_model([
1024
1024
  # $return: The fixed-point notation string
1025
1025
  def _number_to_fixed(args, unused_options):
1026
1026
  x, digits, trim = value_args_validate(_NUMBER_TO_FIXED_ARGS, args)
1027
- result = f'{value_round_number(x, digits):.{int(digits)}f}'
1027
+ result = f'{value_round_number(x, digits):.{digits}f}'
1028
1028
  if trim:
1029
1029
  return R_NUMBER_CLEANUP.sub('', result)
1030
1030
  return result
@@ -1468,7 +1468,7 @@ def _string_char_at(args, unused_options):
1468
1468
  if index >= len(string):
1469
1469
  raise ValueArgsError('index', index)
1470
1470
 
1471
- return string[int(index)]
1471
+ return string[index]
1472
1472
 
1473
1473
  _STRING_CHAR_AT_ARGS = value_args_model([
1474
1474
  {'name': 'string', 'type': 'string'},
@@ -1487,7 +1487,7 @@ def _string_char_code_at(args, unused_options):
1487
1487
  if index >= len(string):
1488
1488
  raise ValueArgsError('index', index)
1489
1489
 
1490
- return ord(string[int(index)])
1490
+ return ord(string[index])
1491
1491
 
1492
1492
  _STRING_CHAR_CODE_AT_ARGS = value_args_model([
1493
1493
  {'name': 'string', 'type': 'string'},
@@ -1564,7 +1564,7 @@ def _string_index_of(args, unused_options):
1564
1564
  if index > len(string):
1565
1565
  raise ValueArgsError('index', index, -1)
1566
1566
 
1567
- return string.find(search, int(index))
1567
+ return string.find(search, index)
1568
1568
 
1569
1569
  _STRING_INDEX_OF_ARGS = value_args_model([
1570
1570
  {'name': 'string', 'type': 'string'},
@@ -1586,7 +1586,7 @@ def _string_last_index_of(args, unused_options):
1586
1586
  if index > len(string):
1587
1587
  raise ValueArgsError('index', index, -1)
1588
1588
 
1589
- return string.rfind(search, 0, int(index) + len(search))
1589
+ return string.rfind(search, 0, index + len(search))
1590
1590
 
1591
1591
  _STRING_LAST_INDEX_OF_ARGS = value_args_model([
1592
1592
  {'name': 'string', 'type': 'string'},
@@ -1645,7 +1645,7 @@ _STRING_NEW_ARGS = value_args_model([
1645
1645
  # $return: The repeated string
1646
1646
  def _string_repeat(args, unused_options):
1647
1647
  string, count = value_args_validate(_STRING_REPEAT_ARGS, args)
1648
- return string * int(count)
1648
+ return string * count
1649
1649
 
1650
1650
  _STRING_REPEAT_ARGS = value_args_model([
1651
1651
  {'name': 'string', 'type': 'string'},
@@ -1686,7 +1686,7 @@ def _string_slice(args, unused_options):
1686
1686
  if end > len(string):
1687
1687
  raise ValueArgsError('end', end)
1688
1688
 
1689
- return string[int(start):int(end)]
1689
+ return string[start:end]
1690
1690
 
1691
1691
  _STRING_SLICE_ARGS = value_args_model([
1692
1692
  {'name': 'string', 'type': 'string'},
@@ -306,12 +306,17 @@ def value_args_validate(fn_args, args, error_return_value=None):
306
306
  arg_lte = fn_arg.get('lte')
307
307
  arg_gt = fn_arg.get('gt')
308
308
  arg_gte = fn_arg.get('gte')
309
- if ((fn_arg.get('integer') and int(arg_value) != arg_value) or
309
+ arg_integer = fn_arg.get('integer')
310
+ if ((arg_integer and int(arg_value) != arg_value) or
310
311
  (arg_lt is not None and not (arg_value < arg_lt)) or
311
312
  (arg_lte is not None and not (arg_value <= arg_lte)) or
312
313
  (arg_gt is not None and not (arg_value > arg_gt)) or
313
314
  (arg_gte is not None and not (arg_value >= arg_gte))):
314
315
  raise ValueArgsError(fn_arg['name'], arg_value, error_return_value)
316
+
317
+ # Normalize integer arguments to int (the parser produces floats for numeric literals)
318
+ if arg_integer:
319
+ args[ix] = int(arg_value)
315
320
  elif arg_type == 'string':
316
321
  arg_value_type = type(arg_value)
317
322
  if arg_value_type is not str:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bare-script
3
- Version: 4.2.2
3
+ Version: 4.2.4
4
4
  Summary: bare-script
5
5
  Home-page: https://github.com/craigahobbs/bare-script
6
6
  Author: Craig A. Hobbs
@@ -219,17 +219,18 @@ cp SKILL.md .claude/skills/bare-script/SKILL.md
219
219
  Use `~/.claude/skills/bare-script/SKILL.md` instead to make it available across all projects. For
220
220
  other assistants, include the file's contents in your system prompt or rules file.
221
221
 
222
- Once installed, you can prompt the assistant with tasks like:
222
+ Once installed, prompt the assistant with a task like:
223
223
 
224
- > Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button
225
- > and a running win/loss/draw tally rendered as a bar chart.
224
+ ```
225
+ claude "Build a MarkdownUp application that plays tic-tac-toe against the user, with a reset button and a running win/loss/draw tally rendered as a bar chart. Save it as ticTacToe.md"
226
+ ```
226
227
 
227
228
  To run the resulting MarkdownUp application locally, install the
228
229
  [markdown-up](https://pypi.org/project/markdown-up/) viewer and point it at the Markdown file:
229
230
 
230
231
  ```
231
232
  pip install markdown-up
232
- markdown-up yourApp.md
233
+ markdown-up ticTacToe.md
233
234
  ```
234
235
 
235
236
  The BareScript library is also documented as single-page Markdown, which can be fetched directly
File without changes
File without changes
File without changes