Merge remote-tracking branch 'upstream/main'
This commit is contained in:
commit
d84bafcdc0
21 changed files with 235 additions and 90 deletions
|
@ -74,6 +74,8 @@ app/javascript/styles/mastodon/reset.scss
|
|||
# Ignore the generated AUTHORS.md
|
||||
AUTHORS.md
|
||||
|
||||
!lint-staged.config.js
|
||||
|
||||
# Ignore glitch-soc emoji map file
|
||||
/app/javascript/flavours/glitch/features/emoji/emoji_map.json
|
||||
|
||||
|
|
|
@ -56,11 +56,6 @@ RSpec/MultipleMemoizedHelpers:
|
|||
RSpec/NestedGroups:
|
||||
Max: 6
|
||||
|
||||
# This cop supports unsafe autocorrection (--autocorrect-all).
|
||||
Rails/ApplicationController:
|
||||
Exclude:
|
||||
- 'app/controllers/health_controller.rb'
|
||||
|
||||
# Configuration parameters: Include.
|
||||
# Include: app/models/**/*.rb
|
||||
Rails/HasAndBelongsToMany:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class HealthController < ActionController::Base
|
||||
class HealthController < ActionController::Base # rubocop:disable Rails/ApplicationController
|
||||
def show
|
||||
render plain: 'OK'
|
||||
end
|
||||
|
|
34
app/javascript/flavours/glitch/components/badge.jsx
Normal file
34
app/javascript/flavours/glitch/components/badge.jsx
Normal file
|
@ -0,0 +1,34 @@
|
|||
import PropTypes from 'prop-types';
|
||||
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
|
||||
import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg';
|
||||
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
|
||||
import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg';
|
||||
|
||||
|
||||
export const Badge = ({ icon, label, domain }) => (
|
||||
<div className='account-role'>
|
||||
{icon}
|
||||
{label}
|
||||
{domain && <span className='account-role__domain'>{domain}</span>}
|
||||
</div>
|
||||
);
|
||||
|
||||
Badge.propTypes = {
|
||||
icon: PropTypes.node,
|
||||
label: PropTypes.node,
|
||||
domain: PropTypes.node,
|
||||
};
|
||||
|
||||
Badge.defaultProps = {
|
||||
icon: <PersonIcon />,
|
||||
};
|
||||
|
||||
export const GroupBadge = () => (
|
||||
<Badge icon={<GroupsIcon />} label={<FormattedMessage id='account.badges.group' defaultMessage='Group' />} />
|
||||
);
|
||||
|
||||
export const AutomatedBadge = () => (
|
||||
<Badge icon={<SmartToyIcon />} label={<FormattedMessage id='account.badges.bot' defaultMessage='Automated' />} />
|
||||
);
|
|
@ -10,6 +10,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
|
|||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||
|
||||
import { Avatar } from 'flavours/glitch/components/avatar';
|
||||
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
|
||||
import { Button } from 'flavours/glitch/components/button';
|
||||
import { Icon } from 'flavours/glitch/components/icon';
|
||||
import { IconButton } from 'flavours/glitch/components/icon_button';
|
||||
|
@ -308,20 +309,17 @@ class Header extends ImmutablePureComponent {
|
|||
const acct = isLocal && domain ? `${account.get('acct')}@${domain}` : account.get('acct');
|
||||
const isIndexable = !account.get('noindex');
|
||||
|
||||
let badge;
|
||||
const badges = [];
|
||||
|
||||
if (account.get('bot')) {
|
||||
badge = (<div className='account-role bot'><FormattedMessage id='account.badges.bot' defaultMessage='Automated' /></div>);
|
||||
badges.push(<AutomatedBadge key='bot-badge' />);
|
||||
} else if (account.get('group')) {
|
||||
badge = (<div className='account-role group'><FormattedMessage id='account.badges.group' defaultMessage='Group' /></div>);
|
||||
} else {
|
||||
badge = null;
|
||||
badges.push(<GroupBadge key='group-badge' />);
|
||||
}
|
||||
|
||||
let role = null;
|
||||
if (account.getIn(['roles', 0])) {
|
||||
role = (<div key='role' className={`account-role user-role-${account.getIn(['roles', 0, 'id'])}`}>{account.getIn(['roles', 0, 'name'])}</div>);
|
||||
}
|
||||
account.get('roles', []).forEach((role) => {
|
||||
badges.push(<Badge key={`role-badge-${role.get('id')}`} label={<span>{role.get('name')}</span>} domain={domain} />);
|
||||
});
|
||||
|
||||
return (
|
||||
<div className={classNames('account__header', { inactive: !!account.get('moved') })} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
||||
|
@ -339,7 +337,6 @@ class Header extends ImmutablePureComponent {
|
|||
<div className='account__header__tabs'>
|
||||
<a className='avatar' href={account.get('avatar')} rel='noopener noreferrer' target='_blank' onClick={this.handleAvatarClick}>
|
||||
<Avatar account={suspended || hidden ? undefined : account} size={90} />
|
||||
{role}
|
||||
</a>
|
||||
|
||||
<div className='account__header__tabs__buttons'>
|
||||
|
@ -356,13 +353,19 @@ class Header extends ImmutablePureComponent {
|
|||
|
||||
<div className='account__header__tabs__name'>
|
||||
<h1>
|
||||
<span dangerouslySetInnerHTML={displayNameHtml} /> {badge}
|
||||
<span dangerouslySetInnerHTML={displayNameHtml} />
|
||||
<small>
|
||||
<span>@{acct}</span> {lockedIcon}
|
||||
</small>
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
{badges.length > 0 && (
|
||||
<div className='account__header__badges'>
|
||||
{badges}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{signedIn && <AccountNoteContainer account={account} />}
|
||||
|
||||
{!(suspended || hidden) && (
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
// It's designed to be emitted in an array format to take up less space
|
||||
// over the wire.
|
||||
|
||||
// This version comment should be bumped each time the emoji data is changed
|
||||
// to ensure that the prevaled file is regenerated by Babel
|
||||
// version: 2
|
||||
|
||||
const { emojiIndex } = require('emoji-mart');
|
||||
let data = require('emoji-mart/data/all.json');
|
||||
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
|
||||
|
@ -15,7 +19,6 @@ const emojiMap = require('./emoji_map.json');
|
|||
const { unicodeToFilename } = require('./unicode_to_filename');
|
||||
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
|
||||
|
||||
|
||||
if(data.compressed) {
|
||||
data = emojiMartUncompress(data);
|
||||
}
|
||||
|
|
|
@ -191,7 +191,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
.account-role,
|
||||
.information-badge,
|
||||
.simple_form .overridden,
|
||||
.simple_form .recommended,
|
||||
|
@ -200,24 +199,55 @@
|
|||
display: inline-block;
|
||||
padding: 4px 6px;
|
||||
cursor: default;
|
||||
border-radius: 3px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
line-height: 12px;
|
||||
font-weight: 500;
|
||||
color: $ui-secondary-color;
|
||||
background-color: var(--user-role-background, rgba($ui-secondary-color, 0.1));
|
||||
border: 1px solid var(--user-role-border, rgba($ui-secondary-color, 0.5));
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&.moderator {
|
||||
color: $success-green;
|
||||
background-color: rgba($success-green, 0.1);
|
||||
border-color: rgba($success-green, 0.5);
|
||||
.information-badge,
|
||||
.simple_form .recommended,
|
||||
.simple_form .not_recommended {
|
||||
background-color: rgba($ui-secondary-color, 0.1);
|
||||
border: 1px solid rgba($ui-secondary-color, 0.5);
|
||||
}
|
||||
|
||||
.account-role {
|
||||
display: inline-flex;
|
||||
padding: 4px;
|
||||
padding-inline-end: 8px;
|
||||
border: 1px solid $highlight-text-color;
|
||||
color: $highlight-text-color;
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.5px;
|
||||
line-height: 16px;
|
||||
gap: 4px;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
|
||||
svg {
|
||||
width: auto;
|
||||
height: 15px;
|
||||
opacity: 0.85;
|
||||
fill: currentColor;
|
||||
}
|
||||
|
||||
&.admin {
|
||||
color: lighten($error-red, 12%);
|
||||
background-color: rgba(lighten($error-red, 12%), 0.1);
|
||||
border-color: rgba(lighten($error-red, 12%), 0.5);
|
||||
&__domain {
|
||||
font-weight: 400;
|
||||
opacity: 0.75;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.information-badge {
|
||||
&.superapp {
|
||||
background-color: rgba($success-green, 0.1);
|
||||
border-color: rgba($success-green, 0.5);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -538,23 +538,25 @@
|
|||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
&__badges {
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
justify-content: space-between;
|
||||
padding: 7px 10px;
|
||||
margin-top: -81px;
|
||||
height: 130px;
|
||||
overflow: hidden;
|
||||
margin-inline-start: -2px; // aligns the pfp with content below
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
|
||||
.account-role {
|
||||
margin: 0 2px;
|
||||
padding: 4px 0;
|
||||
box-sizing: border-box;
|
||||
min-width: 90px;
|
||||
text-align: center;
|
||||
line-height: unset;
|
||||
}
|
||||
}
|
||||
|
||||
&__tabs {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
padding: 7px 10px;
|
||||
margin-top: -55px;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
margin-inline-start: -2px; // aligns the pfp with content below
|
||||
|
||||
&__buttons {
|
||||
display: flex;
|
||||
|
@ -583,10 +585,6 @@
|
|||
&__name {
|
||||
padding: 5px 10px;
|
||||
|
||||
.account-role {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.emojione {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
// It's designed to be emitted in an array format to take up less space
|
||||
// over the wire.
|
||||
|
||||
// This version comment should be bumped each time the emoji data is changed
|
||||
// to ensure that the prevaled file is regenerated by Babel
|
||||
// version: 2
|
||||
|
||||
const { emojiIndex } = require('emoji-mart');
|
||||
let data = require('emoji-mart/data/all.json');
|
||||
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
|
||||
|
@ -15,7 +19,6 @@ const emojiMap = require('./emoji_map.json');
|
|||
const { unicodeToFilename } = require('./unicode_to_filename');
|
||||
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
|
||||
|
||||
|
||||
if(data.compressed) {
|
||||
data = emojiMartUncompress(data);
|
||||
}
|
||||
|
|
11
lint-staged.config.js
Normal file
11
lint-staged.config.js
Normal file
|
@ -0,0 +1,11 @@
|
|||
const config = {
|
||||
'*': 'prettier --ignore-unknown --write',
|
||||
'Capfile|Gemfile|*.{rb,ruby,ru,rake}':
|
||||
'bundle exec rubocop --force-exclusion -a',
|
||||
'*.{js,jsx,ts,tsx}': 'eslint --fix',
|
||||
'*.{css,scss}': 'stylelint --fix',
|
||||
'*.haml': 'bundle exec haml-lint -a',
|
||||
'**/*.ts?(x)': () => 'tsc -p tsconfig.json --noEmit',
|
||||
};
|
||||
|
||||
module.exports = config;
|
|
@ -222,11 +222,5 @@
|
|||
"react-router-dom": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*": "prettier --ignore-unknown --write",
|
||||
"Capfile|Gemfile|*.{rb,ruby,ru,rake}": "bundle exec rubocop --force-exclusion -a",
|
||||
"*.{js,jsx,ts,tsx}": "eslint --fix",
|
||||
"*.{css,scss}": "stylelint --fix"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,7 +79,7 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
it 'returns orderedItems with public or unlisted statuses' do
|
||||
expect(body[:orderedItems]).to be_an Array
|
||||
expect(body[:orderedItems].size).to eq 2
|
||||
expect(body[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
expect(body[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true
|
||||
end
|
||||
|
||||
it_behaves_like 'cacheable response'
|
||||
|
@ -132,7 +132,7 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
json = body_as_json
|
||||
expect(json[:orderedItems]).to be_an Array
|
||||
expect(json[:orderedItems].size).to eq 2
|
||||
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
expect(json[:orderedItems].all? { |item| targets_public_collection?(item) }).to be true
|
||||
end
|
||||
|
||||
it 'returns private Cache-Control header' do
|
||||
|
@ -158,7 +158,7 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
json = body_as_json
|
||||
expect(json[:orderedItems]).to be_an Array
|
||||
expect(json[:orderedItems].size).to eq 3
|
||||
expect(json[:orderedItems].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:to].include?(account_followers_url(account, ActionMailer::Base.default_url_options)) }).to be true
|
||||
expect(json[:orderedItems].all? { |item| targets_public_collection?(item) || targets_followers_collection?(item, account) }).to be true
|
||||
end
|
||||
|
||||
it 'returns private Cache-Control header' do
|
||||
|
@ -217,4 +217,20 @@ RSpec.describe ActivityPub::OutboxesController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ap_public_collection
|
||||
ActivityPub::TagManager::COLLECTIONS[:public]
|
||||
end
|
||||
|
||||
def targets_public_collection?(item)
|
||||
item[:to].include?(ap_public_collection) || item[:cc].include?(ap_public_collection)
|
||||
end
|
||||
|
||||
def targets_followers_collection?(item, account)
|
||||
item[:to].include?(
|
||||
account_followers_url(account, ActionMailer::Base.default_url_options)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -84,7 +84,7 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
expect(page_json).to be_a Hash
|
||||
expect(page_json[:items]).to be_an Array
|
||||
expect(page_json[:items].size).to eq 1
|
||||
expect(page_json[:items].all? { |item| item[:to].include?(ActivityPub::TagManager::COLLECTIONS[:public]) || item[:cc].include?(ActivityPub::TagManager::COLLECTIONS[:public]) }).to be true
|
||||
expect(page_json[:items].all? { |item| targets_public_collection?(item) }).to be true
|
||||
end
|
||||
|
||||
context 'when there are few self-replies' do
|
||||
|
@ -117,8 +117,7 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
|
||||
it 'only inlines items that are local and public or unlisted replies' do
|
||||
inlined_replies = page_json[:items].select { |x| x.is_a?(Hash) }
|
||||
public_collection = ActivityPub::TagManager::COLLECTIONS[:public]
|
||||
expect(inlined_replies.all? { |item| item[:to].include?(public_collection) || item[:cc].include?(public_collection) }).to be true
|
||||
expect(inlined_replies.all? { |item| targets_public_collection?(item) }).to be true
|
||||
expect(inlined_replies.all? { |item| ActivityPub::TagManager.instance.local_uri?(item[:id]) }).to be true
|
||||
end
|
||||
|
||||
|
@ -194,4 +193,14 @@ RSpec.describe ActivityPub::RepliesController do
|
|||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def ap_public_collection
|
||||
ActivityPub::TagManager::COLLECTIONS[:public]
|
||||
end
|
||||
|
||||
def targets_public_collection?(item)
|
||||
item[:to].include?(ap_public_collection) || item[:cc].include?(ap_public_collection)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -12,6 +12,8 @@ RSpec.describe Admin::InstancesController do
|
|||
before do
|
||||
_account_less_popular = Fabricate(:account, domain: 'less.popular')
|
||||
_account_popular_other = Fabricate(:account, domain: 'popular')
|
||||
Instance.refresh
|
||||
|
||||
sign_in current_user, scope: :user
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:follow_recommendation_mute) do
|
||||
account { Fabricate.build(:account) }
|
||||
target_account { Fabricate.build(:account) }
|
||||
end
|
|
@ -339,9 +339,11 @@ RSpec.describe Account do
|
|||
|
||||
results = account.excluded_from_timeline_account_ids
|
||||
expect(results.size).to eq 3
|
||||
expect(results).to include(block.target_account.id)
|
||||
expect(results).to include(mute.target_account.id)
|
||||
expect(results).to include(block_by.account.id)
|
||||
expect(results).to include(
|
||||
block.target_account.id,
|
||||
mute.target_account.id,
|
||||
block_by.account.id
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -1009,27 +1011,4 @@ RSpec.describe Account do
|
|||
expect(subject.reload.followers_count).to eq 15
|
||||
end
|
||||
end
|
||||
|
||||
describe '.followable_by' do
|
||||
context 'with follows and follow requests' do
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:eligible_account) { Fabricate(:account) }
|
||||
let!(:following_account) { Fabricate(:account) }
|
||||
let!(:follow_requested_account) { Fabricate(:account) }
|
||||
|
||||
before do
|
||||
Fabricate :follow, account: account, target_account: following_account
|
||||
Fabricate :follow_request, account: account, target_account: follow_requested_account
|
||||
end
|
||||
|
||||
it 'returns accounts not already following or requested to follow' do
|
||||
results = described_class.followable_by(account)
|
||||
|
||||
expect(results)
|
||||
.to include(eligible_account)
|
||||
.and not_include(following_account)
|
||||
.and not_include(follow_requested_account)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
57
spec/models/account_suggestions/source_spec.rb
Normal file
57
spec/models/account_suggestions/source_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe AccountSuggestions::Source do
|
||||
describe '#base_account_scope' do
|
||||
subject { FakeSource.new }
|
||||
|
||||
before do
|
||||
stub_const 'FakeSource', fake_source_class
|
||||
end
|
||||
|
||||
context 'with follows and follow requests' do
|
||||
let!(:account_domain_blocked_account) { Fabricate(:account, domain: 'blocked.host') }
|
||||
let!(:account) { Fabricate(:account) }
|
||||
let!(:blocked_account) { Fabricate(:account) }
|
||||
let!(:eligible_account) { Fabricate(:account) }
|
||||
let!(:follow_recommendation_muted_account) { Fabricate(:account) }
|
||||
let!(:follow_requested_account) { Fabricate(:account) }
|
||||
let!(:following_account) { Fabricate(:account) }
|
||||
let!(:moved_account) { Fabricate(:account, moved_to_account: Fabricate(:account)) }
|
||||
|
||||
before do
|
||||
Fabricate :account_domain_block, account: account, domain: account_domain_blocked_account.domain
|
||||
Fabricate :block, account: account, target_account: blocked_account
|
||||
Fabricate :follow_recommendation_mute, account: account, target_account: follow_recommendation_muted_account
|
||||
Fabricate :follow_request, account: account, target_account: follow_requested_account
|
||||
Fabricate :follow, account: account, target_account: following_account
|
||||
end
|
||||
|
||||
it 'returns eligible accounts' do
|
||||
results = subject.get(account)
|
||||
|
||||
expect(results)
|
||||
.to include(eligible_account)
|
||||
.and not_include(account_domain_blocked_account)
|
||||
.and not_include(account)
|
||||
.and not_include(blocked_account)
|
||||
.and not_include(follow_recommendation_muted_account)
|
||||
.and not_include(follow_requested_account)
|
||||
.and not_include(following_account)
|
||||
.and not_include(moved_account)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def fake_source_class
|
||||
Class.new described_class do
|
||||
def get(account, limit: 10)
|
||||
base_account_scope(account)
|
||||
.limit(limit)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -13,7 +13,7 @@
|
|||
},
|
||||
"scripts": {
|
||||
"start": "node ./index.js",
|
||||
"check:types": "tsc --noEmit"
|
||||
"typecheck": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
"module": "CommonJS",
|
||||
"moduleResolution": "node",
|
||||
"noUnusedParameters": false,
|
||||
"tsBuildInfoFile": "../tmp/cache/streaming/tsconfig.tsbuildinfo",
|
||||
"paths": {}
|
||||
},
|
||||
"include": ["./*.js", "./.eslintrc.js"]
|
||||
|
|
|
@ -10,6 +10,8 @@
|
|||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"baseUrl": "./",
|
||||
"incremental": true,
|
||||
"tsBuildInfoFile": "tmp/cache/tsconfig.tsbuildinfo",
|
||||
"paths": {
|
||||
"locales": ["app/javascript/locales"],
|
||||
"styles/*": ["app/javascript/styles/*"],
|
||||
|
|
|
@ -2509,9 +2509,9 @@ __metadata:
|
|||
linkType: soft
|
||||
|
||||
"@material-symbols/svg-600@npm:^0.14.0":
|
||||
version: 0.14.3
|
||||
resolution: "@material-symbols/svg-600@npm:0.14.3"
|
||||
checksum: b9d1942790ee420d3464786fc7e748b3925d20c50aa34f2b289807badb85e49d52b4dcb36bed14c45e544a7d25fda81b33dd40d52aa134a6dea74b76805ea863
|
||||
version: 0.14.4
|
||||
resolution: "@material-symbols/svg-600@npm:0.14.4"
|
||||
checksum: 3522055022b6e062399c9da3c701f0306f9f1bd8d5839fbf638fd2e56d97564528ca409901d7599730bf2f67df50754da859a9f499cb7156ead83890511f9d1f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
|
|
Loading…
Reference in a new issue